码迷,mamicode.com
首页 > 其他好文 > 详细

LuoguP5748 集合划分计数

时间:2020-02-03 19:20:59      阅读:77      评论:0      收藏:0      [点我收藏+]

标签:n个元素   swap   its   int   max   gis   char   mat   枚举   

题意

一个有\(n\)个元素的集合,将其分为任意个非空子集,求方案数。集合之间是无序的,\(\{\{1,2\},\{3\}\}=\{\{3\},\{1,2\}\}\)


\(f_n\)表示用\(n\)个元素组成的集合的个数,显然\(f_n=1\)。设\(F(x)\)\(f\)的指数型生成函数,那么\(F(x)=\sum_{i=1}\frac{x^i}{i!}\)\(F^i(x)\)的第\(n\)位就是\(i\)个元素个数之和为\(n\)的集合组合在一起的方案数。

\(g_i\)\(n=i\)时的答案,再设\(G(x)\)\(g\)的指数型生成函数。枚举划分的集合个数:

\[ G(x)=\sum_{i=0}\frac{F^i(x)}{i!} \]

显然\(F(x)=e^x-1\),那么\(G(x)=e^{e^x-1}\)\(G(x)\)\(i\)项乘\(i!\)就是\(g_i\)。多项式\(exp\)即可。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define cn const
#define gc getchar()
#define fp(i,a,b) for(rg int i=(a),ed=(b);i<=ed;++i)
#define fb(i,a,b) for(rg int i=(a),ed=(b);i>=ed;--i)
using namespace std;
typedef cn int cint;
il int rd(){
   rg int x(0),f(1); rg char c(gc);
   while(c<'0'||'9'<c){ if(c=='-') f=-1; c=gc; }
   while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc;
   return x*f;
}
cint inf=0x3f3f3f3f,maxn=1000010,mod=998244353;

int T,n=100000,inv[maxn],fac[maxn],ifac[maxn];
int a[maxn],f[maxn],g[maxn],A[maxn],B[maxn];
int lim=1,l,rev,r[maxn];

il int fpow(int a,int b,int ans=1){
    for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ans=1ll*ans*a%mod;
    return ans;
}
il int finv(cint &n){return fpow(n,mod-2);}
cint G=3,invG=finv(G);

il void ntt(int *a,cint &f){
    fp(i,0,lim-1)if(i<r[i])swap(a[i],a[r[i]]);
    for(rg int md=1;md<lim;md<<=1){
        rg int len=md<<1,Gn=fpow(f?G:invG,(mod-1)/len);
        for(rg int l=0;l<lim;l+=len){
            rg int Pow=1;
            for(rg int nw=0;nw<md;++nw,Pow=1ll*Pow*Gn%mod){
                rg int x=a[l+nw],y=1ll*Pow*a[l+nw+md]%mod;
                a[l+nw]=(x+y)%mod,a[l+nw+md]=(x-y+mod)%mod;
            }
        }
    }
}

void get_inv(int *a,int *b,int n){
    if(n==1){b[0]=finv(a[0]);return;}
    get_inv(a,b,n>>1);
    fp(i,0,n)B[i]=a[i];
    lim=1,l=0; while(lim<=n<<1)lim<<=1,++l; rev=finv(lim);
    fp(i,0,lim-1)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    ntt(B,1),ntt(b,1);
    fp(i,0,lim)b[i]=1ll*b[i]*((2-1ll*B[i]*b[i]%mod+mod)%mod)%mod; ntt(b,0);
    fp(i,0,n)b[i]=1ll*b[i]*rev%mod; fp(i,n+1,lim)b[i]=0;
    fp(i,0,lim)B[i]=0;
}

il void get_ln(int *a,int *b,int n){
    fp(i,1,n)A[i-1]=1ll*a[i]*i%mod; get_inv(a,b,n);
    lim=1,l=0; while(lim<=n<<1)lim<<=1,++l; rev=finv(lim);
    fp(i,0,lim-1)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    ntt(A,1),ntt(b,1);
    fp(i,0,lim)b[i]=1ll*b[i]*A[i]%mod; ntt(b,0);
    fp(i,0,n)b[i]=1ll*b[i]*rev%mod; fp(i,n+1,lim)b[i]=0;
    fp(i,0,lim)A[i]=0;
    fb(i,n,1)b[i]=1ll*b[i-1]*inv[i]%mod; b[0]=0;
}

void get_exp(int *a,int *f,int n){
    if(n==1){f[0]=1;return;}
    get_exp(a,f,n>>1),get_ln(f,g,n);
    g[0]=(1-g[0]+a[0]+mod)%mod; fp(i,1,n)g[i]=(a[i]-g[i]+mod)%mod;
    lim=1,l=0; while(lim<=n<<1)lim<<=1,++l; rev=finv(lim);
    fp(i,0,lim-1)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    ntt(f,1),ntt(g,1);
    fp(i,0,lim)f[i]=1ll*f[i]*g[i]%mod; ntt(f,0);
    fp(i,0,n)f[i]=1ll*f[i]*rev%mod; fp(i,n+1,lim)f[i]=0;
    fp(i,0,lim)g[i]=0;
}

int main(){
    fac[0]=1; fp(i,1,n)fac[i]=1ll*fac[i-1]*i%mod;
    inv[1]=1; fp(i,2,n)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    ifac[0]=1; fp(i,1,n)ifac[i]=1ll*ifac[i-1]*inv[i]%mod;
    
    fp(i,1,n)a[i]=ifac[i];
    while(lim<=n)lim<<=1; get_exp(a,f,lim);
    
    T=rd();
    while(T--)n=rd(),printf("%lld\n",1ll*f[n]*fac[n]%mod);
    return 0;
}

LuoguP5748 集合划分计数

标签:n个元素   swap   its   int   max   gis   char   mat   枚举   

原文地址:https://www.cnblogs.com/akura/p/12256470.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!