HDU 6310Counting Permutations

Posted asuldb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 6310Counting Permutations相关的知识,希望对你有一定的参考价值。

vjudge

为啥正解和暴力跑的差不多快呢;

考虑对于一个给定序列如何求出(displaystyle sum_{i=1}^nmin(i-l_i,r_i-i)),一个简单的想法就是按照最大值分治,我们找到序列中最大值的位置(x),那么(x)的贡献就是(min(x,n+1-x)),之后再对([1,x-1])([x+1,n])分治即可;

不妨按照这个思路进行dp,设(dp_{i,j})表示长度为(i)(displaystyle sum_{k=1}^imin(k-l_k,r_k-k)=j)的排列有多少个,我们枚举一下最大值的位置(x),之后从(i-1)里选(x-1)个分到左边,剩下分到右边,算一波最大值的贡献,分治到左右两边解决就好了,转移大概就是这个样子

[dp_{i,j}=sum_{x=1}^isum_{k=x-1}inom{i-1}{x-1} imes dp_{x-1,k} imes dp_{i-x,j-k-min(x,i+1-x)}]

直接写看起来是(O(n^6))的,但我们理性分析一下发现可能没有那么大。

(F_i)表示长度为(i)的排列的(displaystyle sum_{k=1}^imin(k-l_k,r_k-k))的最大值,(F_i)也可以用按照最大值分治的dp求出来,我们发现(F_n)远没有我们想象的(n^2)级别;理性分析一下,对(F_i)的转移树考虑,发现(min(x,i+1-x))实际上是左右子树中的较小值,于是我们大致可以认为这个复杂度和启发式合并类似,大概是(nlog n)级别,实际上还带一个非常小的常数,当(n=200)时,(F_n)只有(736)。于是上面那个东西直接做的复杂度大概是(O(n^4log^2 n)),暴力卡卡常就已经能过了;

不难注意到(dp_{i})是一个(F_i)次的多项式,证明的话归纳一波就可以;设(G_i(x))表示表示(dp_i)的生成函数,于是有(G_i(x)=displaystyle sum_{j=1}^iinom{i-1}{j-1}G_{j-1}(x)G_{i-j}(x)x^{min(j,i+1-j)}),看起来似乎可以大力fft,但是(O(n^3log^2n))加上巨大常数跑得还没暴力快;fft慢在我们每次都需要DFT过去又IDFT回来,不妨直接将多项式搞成点值的形式,这样中间过程就不需要进行DFT和IDFT,直接点值相乘即可,所以对于每一个(dp_i)维护(0)(F_n)的点值,最后回答询问的时候直接拉格朗日插值回去就能得到系数了;

复杂度是(O(n^3log n+Tn^2log^2 n)),卡卡常才能跑得比暴力快;

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
    char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=201;const int maxw=741;
int mod,lm[maxn],fac[maxw],ifac[maxw],N[11],M[11],T,inv[maxw];
int pw[maxw][maxw],dp[maxn][maxw],lim,cnt,g[maxw],h[maxw],ans;
inline int dqm(int x) {return x<0?x+mod:x;}
inline void upd(int &x,int y) {x+=y;if(x>=mod)x-=mod;}
inline void Lagrange(int n,int m) {
    if(!n) {puts("1");return;}
    memset(g,0,sizeof(g));g[0]=1;ans=0;
    for(re int i=0;i<=lm[n];++i) 
        for(re int j=i;j>=0;--j) {
            upd(g[j+1],g[j]);
            g[j]=1ll*(mod-i)*g[j]%mod;  
        }
    for(re int i=0;i<=lm[n];++i) {
        if(!dp[n][i]) continue;
        int v=1ll*ifac[i]*ifac[lm[n]-i]%mod*dp[n][i]%mod;
        if((lm[n]-i)&1) v=(mod-v)%mod;
        for(re int j=1;j<=lm[n];++j) h[j]=1ll*dqm(h[j-1]-g[j])*inv[i]%mod;
        upd(ans,1ll*h[m]*v%mod);
    }
    printf("%d
",ans);
}
int main() {
    scanf("%d",&mod);fac[0]=inv[1]=ifac[0]=pw[0][0]=1;int x,y;
    while(scanf("%d%d",&x,&y)!=EOF) N[++T]=x,M[T]=y,lim=max(lim,N[T]);
    for(re int i=1;i<=lim;++i)  
        for(re int j=1;j<=i;++j) lm[i]=max(lm[i],lm[j-1]+lm[i-j]+min(j,i+1-j));
    cnt=lm[lim];
    for(re int i=1;i<=cnt;i++) {
        pw[i][0]=1;
        for(re int j=1;j<=cnt;++j) pw[i][j]=1ll*pw[i][j-1]*i%mod; 
    }
    for(re int i=1;i<=cnt;++i)fac[i]=1ll*fac[i-1]*i%mod;
    for(re int i=2;i<=cnt;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(re int i=1;i<=cnt;++i)ifac[i]=1ll*ifac[i-1]*inv[i]%mod;
    for(re int i=0;i<=cnt;++i)dp[0][i]=1;
    for(re int i=1;i<=lim;i++) {
        for(re int j=1;j<=((i+1)>>1);++j) {
            int t=min(j,i+1-j),v=1ll*ifac[j-1]*ifac[i-j]%mod;
            if(j-1!=i-j)upd(v,v);
            for(re int k=0;k<=cnt;k++) 
                upd(dp[i][k],1ll*dp[j-1][k]*dp[i-j][k]%mod*pw[k][t]%mod*v%mod);
        }
        for(re int k=0;k<=cnt;++k) dp[i][k]=1ll*dp[i][k]*fac[i-1]%mod;
    }
    for(re int i=1;i<=T;++i) {
        if(M[i]>lm[N[i]]||M[i]<N[i]) puts("0");
        else Lagrange(N[i],M[i]);
    }
    return 0;
}

以上是关于HDU 6310Counting Permutations的主要内容,如果未能解决你的问题,请参考以下文章

HDU 1264 Counting Squares(模拟)

[HDU3518]Boring counting

[Hdu3887]Counting Offspring

HDU 6184 Counting Stars

[HDU]3518——Boring counting

hdu3518 Boring counting