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

Link

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

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

显然答案为\(m^n\)

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

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

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

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

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

枚举有多少个盒子放了球,然后方案数就是将\(n\)个数划分为若干个无序集合,因此答案为\(\sum\limits_{i=1}^m\left\{n\atop i\right\}\)

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

显然答案为\([n\le m]\)

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

显然答案为\(\left\{n\atop m\right\}\)

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

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

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

显然答案为\({m\choose n}\)

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

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

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

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

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

显然答案为\([n\le m]\)

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

相当于将\(n\)划分为\(m\)个无序正整数的和,答案为\(p(n,m)=[x^{n-m}]\prod\limits_{i=1}^m\frac1{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",&n,&m);
    init(2*std::max(n,m)),calcS(),calcp();
    printf("%d\n",pow(m,n));
    printf("%d\n",mul(C(m,n),fac[n]));
    printf("%d\n",mul(S[m],fac[m]));
    printf("%d\n",std::accumulate(S+1,S+m+1,0,inc));
    printf("%d\n",n<=m);
    printf("%d\n",S[m]);
    printf("%d\n",C(m+n-1,n));
    printf("%d\n",C(m,n));
    printf("%d\n",C(n-1,m-1));
    printf("%d\n",p[n]);
    printf("%d\n",n<=m);
    printf("%d\n",n<m? 0:p[n-m]);
}
posted @ 2020-04-08 14:04  Shiina_Mashiro  阅读(331)  评论(0编辑  收藏  举报