【NOI2017模拟6.22】排列问题

来源:互联网 发布:程序员 工资 编辑:程序博客网 时间:2024/05/21 22:57

题目大意

有n种球,每种球有不同的颜色,第i种球有a[i]个,现在将这些球排成一排,给出q组询问,每组询问给出一个数x,询问满足相邻的球颜色相同的个数为x的排列个数。
设m为所有球的个数和,数据满足:1n,m2×105

题解

设g[i]表示所有球总共被分成i段的方案数(注意,g[i]所描述的每一段的颜色一定是一样的,但是相邻的段的颜色是可能一样的)
如果第i种球被分成了b[i]段,那么方案数就是:(bi)!(bi!)
大力分治一波+ntt就好了
设f是答案
那么,将g调换有:

gi=jifj(ij)

这个式子的意义是:如果被分成了m-i段,那么相邻的球颜色相同的个数至少为i,考虑一个相邻球颜色相同个数位j的排列在其中会被计算(ij)
接着有
gii!=jifjj!1(ji)!

gi调换成gmi,f同样
得到:
gi(mi)!=jifj(mj)!1(ij)!

上面就是一个卷积形式了
设:
g(x)=i=0mgi(mi)!xif(x)=i=0mfi(mi)!xih(x)=i=0m1i!xi

那么有:
g(x)=f(x)h(x)f(x)=g(x)h(x)

所以就要做多项式求逆,然后直接乘就好了,注意转化过程中的颠倒的问题

代码

#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>#include<cmath>#include<set>#include<bitset>#include<map>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)using namespace std;typedef long long LL;typedef double db;int get(){    char ch;    while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');    if (ch=='-'){        int s=0;        while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';        return -s;    }    int s=ch-'0';    while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';    return s;}const int MAXN = 600010;const int MAXL = 20;const int mo = 998244353;const int G = 3;int n,a[MAXN];int st[MAXN],k;int m;int v[MAXN];int A[MAXN],B[MAXN];LL ny[MAXN],js[MAXN],e[MAXN];int g[MAXN],f[MAXN],h[MAXN],H[MAXN];LL d[MAXN];int bitr[MAXN];int N,L;int mi[23];LL quickmi(LL x,LL tim){    LL ans=1;    for(;tim;tim/=2,x=x*x%mo)    if (tim%2)ans=ans*x%mo;    return ans;}void prepare(){    int v=quickmi(G,(mo-1)/N);    d[0]=1;    fo(i,1,N)d[i]=d[i-1]*v%mo;    fo(i,0,N-1){        bitr[i]=0;        fo(j,0,L-1)        if ((i&mi[j])>0)bitr[i]+=mi[L-1-j];    }}int add(int x,int y){    return x+y>=mo?x+y-mo:x+y;}void DFT(int *a){    fo(i,0,N-1)    if (i<bitr[i])swap(a[i],a[bitr[i]]);    for(int now=2;now<=N;now<<=1){        int half=now/2;        fo(i,0,half-1){            int w=d[N/now*i];            for(int j=i;j<N;j+=now){                int l=a[j],r=1ll*w*a[j+half]%mo;                a[j]=add(l,r);                a[j+half]=add(l,(mo-r)%mo);            }        }    }}void IDFT(int *a){    fo(i,0,N-1)    if (i<bitr[i])swap(a[i],a[bitr[i]]);    for(int now=2;now<=N;now<<=1){        int half=now/2;        fo(i,0,half-1){            int w=d[N-N/now*i];            for(int j=i;j<N;j+=now){                int l=a[j],r=1ll*w*a[j+half]%mo;                a[j]=add(l,r);                a[j+half]=add(l,(mo-r)%mo);            }        }    }    LL tmp=quickmi(N,mo-2);    fo(i,0,N-1)a[i]=tmp*a[i]%mo;}void get_nv(int *f,int *f0,int len){    if (len==1){        f0[0]=quickmi(f[0],mo-2);        return;    }    get_nv(f,f0,(len+1)/2);    N=1;L=0;    while(N<=2*len){N<<=1;L++;}    prepare();    fo(i,0,N-1)A[i]=B[i]=0;    fo(i,0,(len+1)/2-1)B[i]=f0[i];    fo(i,0,len-1)A[i]=f[i];    DFT(A);    DFT(B);    fo(i,0,N-1)A[i]=1ll*B[i]*(2ll+mo-1ll*A[i]*B[i]%mo)%mo;    IDFT(A);    fo(i,0,len-1)f0[i]=A[i];}int main(){    freopen("color.in","r",stdin);    freopen("color.out","w",stdout);    mi[0]=1;    fo(i,1,20)mi[i]=mi[i-1]<<1;    n=get();    js[0]=ny[0]=1;    fo(i,1,200000)js[i]=js[i-1]*i%mo;    ny[200000]=quickmi(js[200000],mo-2);    fd(i,199999,1)ny[i]=ny[i+1]*(i+1)%mo;    fo(i,1,200000)e[i]=ny[i]*js[i-1]%mo;    fo(i,1,n){        st[++k]=m+1;        m+=(a[i]=get())+1;        LL now=1;        fo(j,0,a[i]-1){            v[st[k]+j+1]=now*ny[j+1]%mo;            now=now*e[j+1]%mo*(a[i]-1-j)%mo;        }    }    for(N=1,L=0;k>1;N<<=1,L++){        prepare();        int k_=k;        k=0;        for(int now=1,w=1;now<=k_;now=w+1){            if ((now<k_&&a[now]+a[now+1]>=N)||(now==k_)){                st[++k]=st[now];                a[k]=a[now];                w=now;                continue;            }            w=now+1;            fo(i,0,a[now])A[i]=v[st[now]+i];            fo(i,0,a[w])B[i]=v[st[w]+i];            DFT(A);            DFT(B);            fo(i,0,N-1)A[i]=1ll*A[i]*B[i]%mo;            IDFT(A);            fo(i,0,a[now]+a[w])v[st[now]+i]=A[i];            fo(i,0,N-1)A[i]=B[i]=0;            st[++k]=st[now];            a[k]=a[now]+a[w];        }    }    m-=n;    fo(i,0,m)g[i]=1ll*v[i+1]*js[m-i]%mo*js[i]%mo;    fo(i,0,m)h[i]=ny[i];    get_nv(h,H,m+1);    if (N<=2*m){        N<<=1;L++;        prepare();    }    DFT(g);    DFT(H);    fo(i,0,N-1)f[i]=1ll*g[i]*H[i]%mo;    IDFT(f);    fo(i,0,m)f[i]=1ll*f[i]*ny[m-i]%mo;    fo(i,0,m/2)swap(f[i],f[m-i]);    for(int q=get();q;q--){        int x=get();        if (x>m)printf("0\n");        else printf("%d\n",f[x]);    }    fclose(stdin);    fclose(stdout);    return 0;}
原创粉丝点击