十二重计数法

配合多项式全家桶食用最佳

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

每个球都有 \(m\) 种选择,答案就是 \(m^n\)

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

每个盒子最多装一个,也就是说被一个球选中后不能再被选,也就是 \(m^{\underline{n}}\)

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

容斥,枚举多少个盒子不装球,剩下的盒子随便装球。

\[\sum_{i=0}^{m}(-1)^i \dbinom{m}{i}(m-i)^n \]

发现若盒子之间相同的话就变成了斯特林数,所以答案也为 \({n\brace m}m!\)

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

可以理解为 \(n\) 个数划分成 \(m\) 个非空集合,这个就是第二类斯特林数。

\[\sum_{i=1}^{m} {n \brace i} \]

顺便讲下斯特林数·行怎么求。

有性质:

\[m^n=\sum_{i=0}^{m} \dbinom{m}{i}{n\brace i}i! \]

可以无脑二项式反演:

\[\begin{aligned} &f(m) = m^n,g(m)={n\brace m}m! \\ &f(m)=\sum_{i=0}^m \dbinom{m}{i}g(i) \\ &g(m)=\sum_{i=0}^m (-1)^{m-i}\dbinom{m}{i}f(i) \end{aligned} \]

还原得:

\[\begin{aligned} {n\brace m}m! &=\sum_{i=0}^m (-1)^{m-i}\dbinom{m}{i}i^n \\ {n\brace m} &=\sum_{i=0}^m \frac{(-1)^{m-i}\dbinom{m}{i}i^n}{m!} \\ {n\brace m} &=\sum_{i=0}^m \frac{(-1)^{m-i}i^n}{i!(m-i)!} \\ {n\brace m} &=\sum_{i=0}^m \frac{i^n}{i!}\frac{(-1)^{m-i}}{(m-i)!} \end{aligned} \]

这是个卷积,设:

\[A_i=\frac{i^n}{i!},B_i=\frac{(-1)^i}{i!} \]

那么有:

\[{n\brace m}=\sum_{i=0}^m A_iB_{m-i} \]

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

盒子相同,那球随便装,答案就为 \([n\le m]\)

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

每个盒子都必须有球,那就是斯特林数了,答案为 \({n\brace m}\)

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

考虑插板法,总共 \(n+m-1\) 个位置,选出 \(m-1\) 个位置放插板,答案为 \(\dbinom{n+m-1}{m-1}\)

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

相当于选哪些盒子放球,答案为 \(\dbinom{m}{n}\)

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

还是考虑插板法,只不过任何两个板子不能相邻,也就是 \(n-1\) 个位置放,答案为 \(\dbinom{n-1}{m-1}\)

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

十二个中相对较难的。

这个可以理解为整数 \(n\) 划分成 \(m\) 个自然数的方案数。

\(f_{i,j}\) 表示整数 \(i\) 划分成 \(j\) 个自然数的方案数,有转移:

\[f_{i,j}=f_{i-j,j}+f_{i,j-1} \]

意思是给所有数 \(+1\),或添加一个数 \(0\)\(O(n^2)\) 复杂度显然不可过,考虑多项式优化。

\(F_i(x)=f_{0,i}x^0+f_{1,i}x^1+f_{2,i}x^2+...+f_{n,i}x^n\)

那么有转移:

\[\begin{aligned} F_i(x) &= F_{i-1}(x)\times(1+x^i+x^{2i}+...+x^{ki}) \\ &= \frac{F_{i-1}(x)}{1-x^i} \\ &= \prod_{j=1}^{i}\frac{1}{1-x^j} \end{aligned} \]

和付公主的背包很像,考虑套路化乘为加:

\[F_i(x)=\exp\left(\sum_{j=1}^i \ln \frac{1}{1-x^j} \right) \]

关于里面式子取 \(\ln\),设 \(G(x)=\ln \frac{1}{1-x^j}\)

\[\begin{aligned} G(x) &= -\ln(1-x^j) \\ G'(x) &= \frac{jx^{j-1}}{1-x^j} \\ &= jx^{j-1}\sum_{k=0}^{\infty}x^{jk} \\ &= \sum_{k=1}^{\infty}jx^{jk-1} \\ G(x) &= \sum_{k=1}^{\infty}\frac{x^{jk}}{k} \end{aligned} \]

所以:

\[\begin{aligned} F_i(x) &= \exp(\sum_{j=1}^i \sum_{k=1}^{\infty} \frac{x^{jk}}{k}) \\ &= \exp(\sum_{j=1}^i \sum_{j|k} \frac{jx^k}{k}) \end{aligned} \]

到此为止,可以 \(O(n\sqrt{n})\) 时间内求出 \(\ln F_m(x)\),在巨大常数的 \(O(n\log n)\) 时间内求出 \(F_m(x)\)

答案就是 \([x^n]F_m(x)\)

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

\(\text{V}\) 一样,没区别,答案就是 \([n\le m]\)

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

上面 \(\text{X}\) 的划分数是可以为 \(0\) 的,那这里先给所有数 \(+1\),就变成 \(\text{X}\) 了,答案为 \([x^{n-m}]F_m(x)\)

代码巨大长,但我还是贴一下(是我的多项式全家桶,但无用的函数删掉了)。

代码:

#include<bits/stdc++.h>
#define int long long
#define pc(x) putchar(x)
#define clr(f,n) memset(f,0,sizeof(int)*n)
#define cpy(f,g,n) memcpy(f,g,sizeof(int)*n)
#define rev(f,n) reverse(f,f+n)
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
void write(int x)
{
    if(x<0){x=-x;pc('-');}
    if(x>9)write(x/10);
    pc(x%10+48);
}
void polywrite(int *f,int n){for(int i=0;i<n;++i)write(f[i]),pc(' ');pc('\n');}//输出
const int mod=998244353;//模数
const int G=3;//原根
const int maxn=800005;//数组大小,最好算算要开多少,像FDT至少8倍
int qpow(int x,int y=mod-2)//普通快速幂
{
    int res=1;
    while(y)
    {
        if(y&1)res=res*x%mod;
        x=x*x%mod;y>>=1;
    }return res;
}
const int invG=qpow(G);
int n,m,f[maxn],g[maxn],tr[maxn],iv[maxn];
void pref(int m){for(int i=0;i<m;++i)tr[i]=(tr[i>>1]>>1)|((i&1)?m>>1:0);}//初始化
void NTT(int *f,bool op,int m)//逆天天
{
    pref(m);for(int i=0;i<m;++i)if(i<tr[i])swap(f[i],f[tr[i]]);
    for(int p=2;p<=m;p<<=1)
    {
        int len=p>>1;
        int tG=qpow(op?G:invG,(mod-1)/p);
        for(int k=0;k<m;k+=p)
        {
            int buf=1;
            for(int i=k;i<k+len;++i)
            {
                int x=f[i],y=buf*f[i+len]%mod;
                f[i]=(x+y)%mod;f[i+len]=(x-y+mod)%mod;
                buf=buf*tG%mod;
            }
        }
    }if(!op)for(int i=0,invn=qpow(m);i<m;++i)f[i]=f[i]*invn%mod;
}
void px(int *f,int *g,int m){for(int i=0;i<m;++i)f[i]=f[i]*g[i]%mod;}//点乘
void mul(int *f,int *g,int n,int lim)//乘法
{
    static int sav[maxn];
    int m;for(m=1;m<(n<<1);m<<=1);
    clr(sav,m);cpy(sav,g,m);
    NTT(f,1,m);NTT(sav,1,m);
    px(f,sav,m);NTT(f,0,m);
    clr(f+lim,m-lim);clr(sav,m);
}
void invf(int *f,int n)//逆元
{
    int m;for(m=1;m<n;m<<=1);
    static int w[maxn],r[maxn],sav[maxn];
    r[0]=qpow(f[0]);
    for(int p=2;p<=m;p<<=1)
    {
        int len=p>>1;
        cpy(sav,f,p);cpy(w,r,len);
        NTT(sav,1,p);NTT(w,1,p);
        px(sav,w,p);NTT(sav,0,p);
        clr(sav,len);cpy(w,r,p);
        NTT(sav,1,p);NTT(w,1,p);
        px(sav,w,p);NTT(sav,0,p);
        for(int i=len;i<p;++i)r[i]=(mod-sav[i])%mod;
    }cpy(f,r,n);clr(sav,m);clr(w,m);clr(r,m);
}
void deriv(int *f,int n)//求导
{
    for(int i=1;i<n;++i)
        f[i-1]=f[i]*i%mod;
    f[n-1]=0;
}
void integ(int *f,int n)//积分
{
    iv[1]=1;
    for(int i=2;i<=n;++i)
        iv[i]=(mod-mod/i)*iv[mod%i]%mod;
    for(int i=n-1;i>=1;--i)
        f[i]=f[i-1]*iv[i]%mod;
    f[0]=0;
}
void lnf(int *f,int n)//ln
{
    static int sav[maxn];
    cpy(sav,f,n);invf(sav,n);
    deriv(f,n);mul(f,sav,n,n);
    integ(f,n);clr(sav,n);
}
void expf(int *f,int n)//exp
{
    static int sav[maxn],s[maxn];
    int m;for(m=1;m<n;m<<=1); s[0]=1;
    for(int p=2;p<=m;p<<=1)
    {
        cpy(sav,s,p<<1);lnf(sav,p);
        for(int i=0;i<p;++i)
            sav[i]=(f[i]-sav[i]+mod)%mod;
        (sav[0]+=1)%=mod;mul(s,sav,p,p);
    }cpy(f,s,n);clr(sav,m);clr(s,m);
}
void qpowf(int *f,int n,int k)//快速幂
{
    lnf(f,n);
    for(int i=0;i<n;++i)f[i]=f[i]*k%mod;
    expf(f,n);
}
int fac[400005],ifac[400005],S[200005],F[200005];
void init()
{
    fac[0]=iv[1]=1;
    for(int i=1;i<=n+m;++i)fac[i]=fac[i-1]*i%mod;
    for(int i=2;i<=n+m;++i)iv[i]=(mod-mod/i)*iv[mod%i]%mod;
    ifac[n+m]=qpow(fac[n+m]);
    for(int i=n+m-1;i>=0;--i)ifac[i]=ifac[i+1]*(i+1)%mod;
}
int C(int x,int y){return x<y?0:fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
void solve4()
{
    int res=0;
    for(int i=1;i<=m;++i)
        res=(res+S[i])%mod;
    write(res),pc('\n');
}
signed main()
{
    n=read(),m=read();init();
    for(int i=0,tmp=1;i<=m;++i)
    {
        f[i]=qpow(i,n)*ifac[i]%mod;
        g[i]=i&1?mod-ifac[i]:ifac[i];
    }mul(f,g,m+1,m+1);
    for(int i=0;i<=m;++i)S[i]=f[i];
    memset(f,0,sizeof f);
    for(int i=1;i<=m;++i)
        for(int j=i;j<=n;j+=i)
            f[j]=(f[j]+iv[j/i])%mod;
    expf(f,n+1);for(int i=0;i<=n;++i)F[i]=f[i];
    write(qpow(m,n)),pc('\n');
    write(fac[m]*ifac[m-n]%mod),pc('\n');
    write(S[m]*fac[m]%mod),pc('\n');
    solve4();
    write(n<=m),pc('\n');
    write(S[m]),pc('\n');
    write(C(n+m-1,n)),pc('\n');
    write(C(m,n)),pc('\n');
    write(C(n-1,m-1)),pc('\n');
    write(F[n]),pc('\n');
    write(n<=m),pc('\n');
    write(n<m?0:F[n-m]),pc('\n');
    return 0;
}

posted @ 2022-09-16 20:06  violetctl39  阅读(127)  评论(0编辑  收藏  举报