Discriptionc++
在2016年,佳媛姐姐刚刚学习了第二类斯特林数,很是开心。函数
输入只有一个正整数学习
Output输出f(n)。spa
因为结果会很大,输出f(n)对998244353(7 × 17 × 223 + 1)取模的结果便可。code
1 ≤ n ≤ 100000blog
Sample InputSample Outputip
87it
咱们知道第二类斯特林数和排列数(降低幂)组合在一块儿能够表示n^k,又由于排列等于组合乘上一个阶乘,因而咱们就能够开开心心的二项式反演,获得一个某一行(其实也能够不少行,鉴于这个式子的特殊性质,咱们能够把不一样行的同一列合并)某一列的斯特林数的表达式。io
具体的说,S(k,n) = Σ (i^k / i!) * ((-1)^(n-i) / (n-i)!) [具体推导就不写了,就是一个二项式反演]。class
这个式子的特殊性质太多了,首先它是一个卷积的形式,因此咱们能够直接用NTT 在 N log N 的时间求出某一行的全部第二类斯特林数分别是多少;
而且只有 i^k 项和行数有关,因此同一列很好合并,因而这个题就作完了2333。
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=330005; const int ha=998244353; const int root=3,inv=ha/3+1; int a[maxn],b[maxn],jc[maxn]; int r[maxn],N,M,n,INV,l; inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x; } inline int ksm(int x,int y){ int an=1; for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha; return an; } inline void NTT(int *c,const int f){ for(int i=0;i<N;i++) if(i<r[i]) swap(c[i],c[r[i]]); for(int i=1;i<N;i<<=1){ int omega=ksm(f==1?root:inv,(ha-1)/(i<<1)); for(int p=i<<1,j=0;j<N;j+=p){ int now=1; for(int k=0;k<i;k++,now=now*(ll)omega%ha){ int x=c[j+k],y=c[j+k+i]*(ll)now%ha; c[j+k]=add(x,y); c[j+k+i]=add(x,ha-y); } } } if(f==-1) for(int i=0;i<N;i++) c[i]=c[i]*(ll)INV%ha; } inline void init(){ jc[0]=1; for(int i=1;i<=n;i++) jc[i]=jc[i-1]*(ll)i%ha; for(int i=0;i<=n;i++){ if(!i) a[i]=1; else if(i==1) a[i]=n+1; else a[i]=add(ksm(i,n+1),ha-1)*(ll)ksm(add(i,ha-1)*(ll)jc[i]%ha,ha-2)%ha; if(i&1) b[i]=ha-ksm(jc[i],ha-2); else b[i]=ksm(jc[i],ha-2); } M=n<<1; for(N=1;N<=M;N<<=1) l++; for(int i=0;i<N;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1)); } inline void solve(){ NTT(a,1),NTT(b,1); for(int i=0;i<N;i++) a[i]=a[i]*(ll)b[i]%ha; INV=ksm(N,ha-2),NTT(a,-1); } inline void output(){ int ans=0,base=1; for(int i=0;i<=n;i++,base=add(base,base)) ans=add(ans,a[i]*(ll)base%ha*(ll)jc[i]%ha); printf("%d\n",ans); } int main(){ scanf("%d",&n); init(); solve(); output(); return 0; }