用到第二类斯特林数的性质,做法好像很多,我打的是直接ntt,由第二类斯特林数的容斥公式可以推出,我们可以对于每一个i,来一次ntt求出他与所有j组成的第二类斯特林数的值,这个时候我们是O(n^2logn)的,还不如暴力,但是我们发现,对于刚刚提到的容斥的式子,将其化为卷积形式后,其一边的每一项对于每一个i都相同,另一边的每一项是对于所有的i形成一个n项的等比数列,这样我们可以把成等比数列的一边求和,用固定的一边去卷他们的和,这时候的答案的每一项就是所有的i的这一项的和,然后我们再O(n)乘上阶乘和2的次幂就可以了.
(一开始代码打错了,还以为那个公式在S(i,j)不存在的时候是错的……后来手玩了一下才发现他是对的……)
#include <cstdio> #include <cstring> #include <algorithm> const int N=400010; const int P=998244353; typedef long long LL; inline int Pow(int x,int y){ int ret=1; while(y){ if(y&1)ret=(LL)ret*x%P; x=(LL)x*x%P,y>>=1; }return ret; } int A[N],B[N],rev[N],len; int ai[N],bi[N],ci[N]; int jie[N],ni[N],inv[N],n; inline void ntt(int *C,int opt){ register int i,j,k,w;int wn,temp; for(i=1;i<len;++i)if(i<rev[i])std::swap(C[i],C[rev[i]]); for(k=2;k<=len;k<<=1){ wn=Pow(3,(P-1)/k); if(opt==-1)wn=Pow(wn,P-2); for(i=0;i<len;i+=k){ w=1; for(j=0;j<(k>>1);++j,w=(LL)w*wn%P){ temp=(LL)w*C[i+j+(k>>1)]%P; C[i+j+(k>>1)]=(C[i+j]-temp+P)%P; C[i+j]=(C[i+j]+temp)%P; } } } } inline void mul(int *a,int *b,int *c,int n){ len=1;while(len<n)len<<=1;int i; for(i=1;i<len;++i)rev[i]=(rev[i>>1]>>1)|((i&1)?(len>>1):0); for(i=0;i<len;++i)A[i]=a[i],B[i]=b[i]; ntt(A,1),ntt(B,1); for(i=0;i<len;++i)A[i]=(LL)A[i]*B[i]%P; ntt(A,-1); int Inv=Pow(len,P-2); for(i=0;i<len;++i)c[i]=(LL)A[i]*Inv%P; } int main(){ scanf("%d",&n);int i,ans=1,temp=1; jie[0]=ni[0]=1,inv[1]=1; for(i=2;i<=n;++i)inv[i]=((-(LL)(P/i)*inv[P%i])%P+P)%P; for(i=1;i<=n;++i)jie[i]=(LL)jie[i-1]*i%P,ni[i]=(LL)ni[i-1]*inv[i]%P; bi[0]=0,bi[1]=n,ai[0]=1,ai[1]=P-1; for(i=2;i<=n;++i) bi[i]=(LL)i*(Pow(i,n)-1+P)%P*ni[i]%P*inv[i-1]%P,ai[i]=(i&1)?(P-ni[i]):ni[i]; mul(ai,bi,ci,n+n+2); for(i=1;i<=n;++i) temp=(((LL)temp)<<1LL)%P,ans=(ans+(LL)ci[i]*temp%P*jie[i])%P; printf("%d\n",ans); return 0; }