其他分享
首页 > 其他分享> > 典中典之第二类斯特林数

典中典之第二类斯特林数

作者:互联网

第二类斯特林数:将 \(n\) 个物品放进 \(m\) 个不区分的盒子的方案数,记为 \(S(n,m)\)。

\(n^2\) 递推公式:\(S(n,m)=S(n-1,m-1)+m\cdot S(n-1,m)\).

附代码:

s[0][0]=1;
for(int i=1;i<=n;++i)
    for(int j=1;j<=i;++j)
        s[i][j]=add(s[i-1][j-1],mul(s[i-1][j],j));

第二类斯特林数的卷积公式:

\[S(n,m)=\sum_{k=0}^m \frac{(-1)^k}{k!}\frac{(m-k)^n}{(m-k)!} \]

斯特林数转下降幂公式:

\[x^k=\sum_{i=0}^kS(k,i)x^{\underline i} \]

又已知一常见组合公式(证明展开即可):

\[\binom{n}{x} x^{\underline i}=\binom{n-i}{x-i}n^{\underline i} \]

将以上两式合并则有:

\[\binom{n}{x}x^k = \sum_{i=0}^k S(k,i)\binom{n-i}{x-i} n^{\underline i} \]

对于枚举 \(x\) 的题目,一般只需交换求和号后将后面的组合数用二项式定理合并成 \(n-i\) 次多项式即可,这样可以将时间复杂度由 \(n\) 降到 \(k\)。

例1:CF932E Team Work

从 \(n\) 个物品中挑任意个,挑 \(x\) 个的价值为 \(x^k\)。求所有方案的价值和。\(n\le 10^9, k\le 5000\).

即求

\[\sum_{x=0}^n \binom{n}{x} x^k \]

由前文式子可得

\[=\sum_{x=0}^n \sum_{i=0}^k S(k,i)\binom{n-i}{x-i} n^{\underline i} \]

交换求和号

\[=\sum_{i=0}^{\min(k,n)} S(k,i)n^{\underline i}\sum_{x=i}^{n} \binom{n-i}{x-i} \]

改为枚举 \(x-i\)

\[=\sum_{i=0}^{\min(k,n)} S(k,i)n^{\underline i}\sum_{x=0}^{n-i} \binom{n-i}{x} \]

后半部分求和用二项式定理合并,即为

\[=\sum_{i=0}^{\min(k,n)} S(k,i)n^{\underline i} 2^{n-i} \]

\(O(k^2)\) 预处理第二类斯特林数后 \(O(k)\) 求解即可。

#include<bits/stdc++.h>
using namespace std;
const int N=5005,Mod=1e9+7;
#define mul(x,y) (1ll*(x)*(y)%Mod)
inline int add(int x, int y)
{
    return (x+y>=Mod?x+y-Mod:x+y);
}
inline int po(int x, long long y)
{
    int r=1;
    for(;y;y>>=1,x=mul(x,x)) if(y&1) r=mul(r,x);
    return r;
}
int s[N][N],n,m,k;
int main()
{
    int n,k; scanf("%d%d",&n,&k);
    s[0][0]=1;
    for(int i=1;i<=k;++i)
        for(int j=1;j<=i;++j)
            s[i][j]=add(s[i-1][j-1],mul(s[i-1][j],j));
    int ans=0;
    for(int i=0,b=1,c=po(2,n),iv=po(2,Mod-2);i<=min(n,k);++i)
    {
        int t=mul(s[k][i],mul(b,c));
        ans=add(ans,t);
        b=mul(b,n-i), c=mul(c,iv);
    }
    printf("%d\n",ans);
}

例2:CF1716F Bags with Balls

有 \(n\) 个盒子,每个盒子里放着编号 \(1\sim m\) 的球。每次从每个盒子里分别抽取 1 个球,令抽到的编号为奇数的球的个数为 \(x\),则价值为 \(x^k\)。求所有可能方案的价值之和。\(T\) 组数据,\(n,m<998244353, k\le 2000\).

即为求

\[\sum_{x=0}^n x^k \binom{n}{x} \lceil\frac{m}{2}\rceil^x \lfloor \frac{m}{2}\rfloor ^ {n-x} \]

由上文结论易推得

\[=\sum_{i=0}^{\min(k,n)} S(k,i)n^{\underline i}\sum_{x=0}^{n-i} \binom{n-i}{x} \lceil\frac{m}{2}\rceil^{x+i} \lfloor \frac{m}{2}\rfloor ^ {n-i-x} \]

提取一个 \(\lceil\frac{m}{2}\rceil^i\),后面用二项式定理合并,即为 \(m^{n-i}\)。

\[=\sum_{i=0}^{\min(k,n)} S(k,i)n^{\underline i} \lceil\frac{m}{2}\rceil^i m^{n-i} \]

线性计算即可。复杂度 \(O(k^2+Tk)\)。

#include<bits/stdc++.h>
using namespace std;
const int N=2022,Mod=998244353;
#define mul(x,y) (1ll*(x)*(y)%Mod)
inline int add(int x, int y)
{
    return (x+y>=Mod?x+y-Mod:x+y);
}
inline int po(int x, long long y)
{
    int r=1;
    for(;y;y>>=1,x=mul(x,x)) if(y&1) r=mul(r,x);
    return r;
}
int s[N][N],n,m,k;
void Init()
{
    const int n=2000;
    s[0][0]=1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=i;++j)
            s[i][j]=add(s[i-1][j-1],mul(s[i-1][j],j));
}
int main()
{
    Init();
    int T; scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        const int o=(m-1)/2+1;
        int ans=0;
        for(int i=0,b=1,c=1,d=po(m,n),iv=po(m,Mod-2);i<=min(n,k);++i)
        {
            int t=mul(s[k][i],mul(mul(b,c),d));
            ans=add(ans,t);
            b=mul(b,n-i), c=mul(c,o), d=mul(d,iv);
        }
        printf("%d\n",ans);
    }
}

待填坑。

标签:第二类,frac,斯特林,sum,int,典中典,binom,underline,Mod
来源: https://www.cnblogs.com/farway17/p/16574471.html