bzoj 4555: [Tjoi2016&Heoi2016]求和 NTT

来源:互联网 发布:200元左右的礼品 知乎 编辑:程序博客网 时间:2024/06/05 00:42

题意

计算这样一个函数的值:
这里写图片描述
S(i, j)表示第二类斯特林数,递推公式为:
S(i, j) = j ∗ S(i − 1, j) + S(i − 1, j − 1), 1 <= j <= i − 1。
边界条件为:S(i, i) = 1(0 <= i), S(i, 0) = 0(1 <= i)
你能帮帮他吗?
n<=100000

分析

第二类斯特林数S(n,m)在组合数学中的意义就是把n个不同的球放入m个相同的盒子里的方案数。那么显然S(n,m)*m!就是把n个不同的球放到m个不同的盒子里的方案数。
考虑容斥:
若可以有空盒子则方案数为mn
简单容斥一下即可得到

S(n,m)=1m!k=0mCkm(mk)n(1)k

带进题目所给式子可以得到
ans=i=0nj=0n2jk=0jCkj(jk)i(1)k

将组合数拆开可得
ans=i=0nj=0n2jj!k=0j(jk)i(jk)!(1)kk!

将第一个sigma移到后面去:
ans=j=0n2jj!k=0jni=0(jk)i(jk)!(1)kk!

那么这就变成了一个卷积形式,直接上NTT即可。

调了超久都不知道哪里错,最后发现原来NTT的过程中的减法是不能把负数变成正数的。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int MAXN=300005;const int MOD=998244353;int n,jc[MAXN],ny[MAXN],a[MAXN],b[MAXN],rev[MAXN],N,lg;int ksm(int x,int y){    int ans=1;    while (y)    {        if (y&1) ans=(LL)ans*x%MOD;        x=(LL)x*x%MOD;y>>=1;    }    return ans;}void NTT(int *a,int f){    for (int i=0;i<N;i++) if (i<rev[i]) swap(a[i],a[rev[i]]);    for (int i=1;i<N;i<<=1)    {        int wn=ksm(3,f==1?(MOD-1)/i/2:MOD-1-(MOD-1)/i/2);        for (int j=0;j<N;j+=(i<<1))        {            int w=1;            for (int k=0;k<i;k++)            {                int u=a[j+k],v=(LL)w*a[j+k+i]%MOD;                a[j+k]=(u+v)%MOD;a[j+k+i]=(u-v)%MOD;                w=(LL)w*wn%MOD;            }        }    }    int ny=ksm(N,MOD-2);    if (f==-1) for (int i=0;i<N;i++) a[i]=(LL)a[i]*ny%MOD;}int main(){    scanf("%d",&n);    jc[0]=ny[0]=1;    for (int i=1;i<=n;i++)    {        jc[i]=(LL)jc[i-1]*i%MOD;        ny[i]=ksm(jc[i],MOD-2);    }    for (int i=0;i<=n;i++)    {        if (i%2==0) a[i]=ny[i];        else a[i]=-ny[i];        b[i]=(LL)(ksm(i,n+1)-1)*ksm(i-1,MOD-2)%MOD*ny[i]%MOD;    }    b[1]=n+1;    for (N=1;N<=n*2;N*=2,lg++);    for (int i=0;i<N;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));    NTT(a,1);NTT(b,1);    for (int i=0;i<N;i++) a[i]=(LL)a[i]*b[i]%MOD;    NTT(a,-1);    int ans=0,now=1;    for (int i=0;i<=n;i++)    {        ans=(ans+(LL)now*jc[i]%MOD*a[i]%MOD)%MOD;        now=now*2%MOD;    }    printf("%d",(ans+MOD)%MOD);    return 0;}
0 0