Luogu P5824 十二重计数法(小球盒子计数)

Link

假如有(n)个球,要放进(m)个盒子,求方案数。

( ext{I}):球之间互不相同,盒子之间互不相同。

显然答案为(m^n)

( ext{II}):球之间互不相同,盒子之间互不相同,每个盒子至多装一个球。

依次把球放进盒子,放第(i)个球时有(m-i+1)种方案,因此答案为(m^{underline n})

( ext{III}):球之间互不相同,盒子之间互不相同,每个盒子至少装一个球。

相当于把(n)个数划分为(m)个有序集合,因此答案为(left{natop m ight}m!)

( ext{IV}):球之间互不相同,盒子全部相同。

枚举有多少个盒子放了球,然后方案数就是将(n)个数划分为若干个无序集合,因此答案为(sumlimits_{i=1}^mleft{natop i ight})

( ext{V}):球之间互不相同,盒子全部相同,每个盒子至多装一个球。

显然答案为([nle m])

( ext{VI}):球之间互不相同,盒子全部相同,每个盒子至少装一个球。

显然答案为(left{natop m ight})

( ext{VII}):球全部相同,盒子之间互不相同。

每个盒子装任意个球都只有一种方案,OGF为(frac1{1-x}),因此答案为([x^n]frac1{(1-x)^m}={n+m-1choose n})

( ext{VIII}):球全部相同,盒子之间互不相同,每个盒子至多装一个球。

显然答案为({mchoose n})

( ext{IX}):球全部相同,盒子之间互不相同,每个盒子至少装一个球。

此时每个盒子装球的OGF为(frac x{1-x}),因此答案为([x^n]frac{x^m}{(1-x)^m}={n-1choose m-1})

( ext{X}):球全部相同,盒子全部相同。

相当于将(n)划分为(m)个无序自然数的和,答案为(p(n+m,m)=[x^n]prodlimits_{i=1}^mfrac1{1-x^i})

( ext{XI}):球全部相同,盒子全部相同,每个盒子至多装一个球。

显然答案为([nle m])

( ext{XII}):球全部相同,盒子全部相同,每个盒子至少装一个球。

相当于将(n)划分为(m)个无序正整数的和,答案为(p(n,m)=[x^{n-m}]prodlimits_{i=1}^mfrac1{1-x^i})

#include<cstdio>
#include<cstring>
#include<numeric>
#include<algorithm>
const int N=524289,P=998244353;
int n,m,deg,len,fac[N],inv[N],ifac[N],rev[N],w[N],S[N],p[N];
int inc(int a,int b){return a+=b-P,a+=a>>31&P;}
int dec(int a,int b){return a-=b,a+=a>>31&P;}
int mul(int a,int b){return 1ll*a*b%P;}
int pow(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
int C(int n,int m){return m<0||m>n? 0:mul(mul(fac[n],ifac[m]),ifac[n-m]);}
int getlen(int n){return 1<<(32-__builtin_clz(n));}
void init(int n)
{
    int lim=1<<(len=32-__builtin_clz(n)),g=pow(3,(P-1)/lim);
    w[lim>>1]=1,fac[0]=ifac[0]=inv[0]=fac[1]=ifac[1]=inv[1]=1;
    for(int i=1;i<lim;++i) rev[i]=(rev[i>>1]>>1)|(i&1? lim>>1:0);
    for(int i=(lim>>1)+1;i<lim;++i) w[i]=mul(w[i-1],g);
    for(int i=(lim>>1)-1;i;--i) w[i]=w[i<<1];
    for(int i=2;i<=lim;++i) fac[i]=mul(fac[i-1],i),ifac[i]=mul(ifac[i-1],inv[i]=mul(inv[P%i],P-P/i));
}
void NTT(int*a,int lim,int f)
{
    if(!~f) std::reverse(a+1,a+lim);
    for(int i=0,x=len-__builtin_ctz(lim);i<lim;++i) if(i<rev[i]>>x) std::swap(a[i],a[rev[i]>>x]);
    for(int i=1;i<lim;i<<=1) for(int j=0,d=i<<1;j<lim;j+=d) for(int k=0,x;k<i;++k) x=mul(a[i+j+k],w[i+k]),a[i+j+k]=dec(a[j+k],x),a[j+k]=inc(a[j+k],x);
    if(!~f) for(int i=0,x=P-(P-1)/lim;i<lim;++i) a[i]=mul(a[i],x);
}
void Inv(int*a,int*b,int deg)
{
    if(deg==1) return b[0]=pow(a[0],P-2),void();
    static int t[N];int lim=getlen(deg*2-2);
    Inv(a,b,(deg+1)>>1),memcpy(t,a,deg<<2),memset(t+deg,0,(lim-deg)<<2);
    NTT(t,lim,1),NTT(b,lim,1);
    for(int i=0;i<lim;++i) b[i]=mul(dec(2,mul(b[i],t[i])),b[i]);
    NTT(b,lim,-1),memset(b+deg,0,(lim-deg)<<2);
}
void Der(int*a,int*b,int deg){for(int i=1;i<deg;++i)b[i-1]=mul(a[i],i);b[deg-1]=0;}
void Int(int*a,int*b,int deg){for(int i=1;i<deg;++i)b[i]=mul(a[i-1],inv[i]);b[0]=0;}
void Ln(int*a,int*b,int deg)
{
    static int t[N];int lim=getlen(deg*2-2);
    Inv(a,t,deg),Der(a,b,deg),NTT(t,lim,1),NTT(b,lim,1);
    for(int i=0;i<lim;++i) t[i]=mul(t[i],b[i]);
    NTT(t,lim,-1),Int(t,b,deg),memset(t,0,lim<<2),memset(b+deg,0,(lim-deg)<<2);
}
void Exp(int*a,int*b,int deg)
{
    if(deg==1) return b[0]=1,void();
    static int t[N];int lim=getlen(deg*2-2);
    Exp(a,b,(deg+1)>>1),Ln(b,t,deg);
    for(int i=0;i<deg;++i) t[i]=dec(a[i],t[i]);
    memset(t+deg,0,(lim-deg)<<2),++t[0],NTT(t,lim,1),NTT(b,lim,1);
    for(int i=0;i<lim;++i) b[i]=mul(b[i],t[i]);
    NTT(b,lim,-1),memset(b+deg,0,(lim-deg)<<2),memset(t+deg,0,(lim-deg)<<2);
}
void calcS()
{
    static int F[N],G[N],lim=1<<len,deg=std::min(n,m)+1;
    for(int i=0;i<deg;++i) F[i]=mul(pow(i,n),ifac[i]),G[i]=i&1? P-ifac[i]:ifac[i];
    NTT(F,lim,1),NTT(G,lim,1);
    for(int i=0;i<lim;++i) S[i]=mul(F[i],G[i]);
    NTT(S,lim,-1),memset(S+deg,0,(lim-deg)*4);
}
void calcp()
{
    static int F[N];
    for(int i=1;i<=m;++i) for(int j=i;j<=n;j+=i) F[j]=inc(F[j],inv[j/i]);
    Exp(F,p,n+1);
}
int main()
{
    scanf("%d%d
",&n,&m);
    init(2*std::max(n,m)),calcS(),calcp();
    printf("%d
",pow(m,n));
    printf("%d
",mul(C(m,n),fac[n]));
    printf("%d
",mul(S[m],fac[m]));
    printf("%d
",std::accumulate(S+1,S+m+1,0,inc));
    printf("%d
",n<=m);
    printf("%d
",S[m]);
    printf("%d
",C(m+n-1,n));
    printf("%d
",C(m,n));
    printf("%d
",C(n-1,m-1));
    printf("%d
",p[n]);
    printf("%d
",n<=m);
    printf("%d
",n<m? 0:p[n-m]);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12659575.html