bzoj4555 [Tjoi2016&Heoi2016]求和
Description
在2016年,佳媛姐姐刚刚学习了第二类斯特林数,非常开心。
现在他想计算这样一个函数的值:
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)
你能帮帮他吗?
Input
输入只有一个正整数
Output
输出f(n)。由于结果会很大,输出f(n)对998244353(7 × 17 × 223 + 1)取模的结果即可。1 ≤ n ≤ 100000
Sample Input
3
Sample Output
87
正解:组合数学+分治$FFT$。
很久以前看到这道题,当时连第二类斯特林数是什么都不知道,于是什么都不会。现在想想,还是不难的。。
我们考虑第二类斯特林数的意义:把$n$个球放入$m$个无区别的盒子里的方案数。
上式有一个$m!$,那就是把无区别的盒子变成有区别的盒子,$2^{m}$表示把盒子染成黑白两色。
那么我们考虑$g[n]$为把$n$个球分成任意个有区别的集合,且每个集合任意染黑白两色的方案数。显然,$Ans=\sum_{i=0}^{n}g(i)$。
根据意义,我们可以推出$g(n)$的递推式:$g(n)=\sum_{i=1}^{n}2*\binom{n}{i}*g(n-i)$。
枚举第$1$个集合有多少元素,且颜色是什么,就可以推出上式了。
然后我们直接用分治$FFT$就可以求出$g$函数了。
1 #include <bits/stdc++.h> 2 #define il inline 3 #define RG register 4 #define ll long long 5 #define rhl (998244353) 6 #define N (500010) 7 #define G (3) 8 9 using namespace std; 10 11 int f[N],g[N],w[N],fac[N],ifac[N],inv[N],rev[N],a[N],b[N],n,ans; 12 13 il int gi(){ 14 RG int x=0,q=1; RG char ch=getchar(); 15 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 16 if (ch=='-') q=-1,ch=getchar(); 17 while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 18 return q*x; 19 } 20 21 il int qpow(RG int a,RG int b){ 22 RG int ans=1; 23 while (b){ 24 if (b&1) ans=1LL*ans*a%rhl; 25 a=1LL*a*a%rhl,b>>=1; 26 } 27 return ans; 28 } 29 30 il void pre(){ 31 fac[0]=ifac[0]=fac[1]=ifac[1]=inv[1]=1,g[1]=2; 32 for (RG int i=2;i<=n;++i){ 33 inv[i]=1LL*(rhl-rhl/i)*inv[rhl%i]%rhl; 34 fac[i]=1LL*fac[i-1]*i%rhl; 35 ifac[i]=1LL*ifac[i-1]*inv[i]%rhl; 36 g[i]=ifac[i]<<1; if (g[i]>=rhl) g[i]-=rhl; 37 } 38 for (RG int i=1,v=0;i<(n<<2);i<<=1,++v) w[v]=qpow(G,(rhl-1)/(i<<1)); 39 return; 40 } 41 42 il void NTT(int *a,RG int n,RG int f){ 43 for (RG int i=0;i<n;++i) if (i<rev[i]) swap(a[i],a[rev[i]]); 44 for (RG int i=1,v=0;i<n;i<<=1,++v){ 45 RG int gn=w[v],x,y; 46 for (RG int j=0;j<n;j+=i<<1){ 47 RG int g=1; 48 for (RG int k=0;k<i;++k,g=1LL*g*gn%rhl){ 49 x=a[j+k],y=1LL*g*a[j+k+i]%rhl; 50 a[j+k]=x+y; if (a[j+k]>=rhl) a[j+k]-=rhl; 51 a[j+k+i]=x-y; if (a[j+k+i]<0) a[j+k+i]+=rhl; 52 } 53 } 54 } 55 if (f==1) return; reverse(a+1,a+n); RG int inv=qpow(n,rhl-2); 56 for (RG int i=0;i<n;++i) a[i]=1LL*a[i]*inv%rhl; return; 57 } 58 59 il void solve(RG int l,RG int r){ 60 if (l==r){ if (!l) f[l]=1; return; } 61 RG int mid=(l+r)>>1,len,lg=0; solve(l,mid); 62 for (len=1;len<=r-l+1;len<<=1) ++lg; 63 for (RG int i=0;i<len;++i) rev[i]=rev[i>>1]>>1|((i&1)<<(lg-1)),a[i]=b[i]=0; 64 for (RG int i=l;i<=mid;++i) a[i-l]=f[i]; 65 for (RG int i=0;i<=r-l;++i) b[i]=g[i]; NTT(a,len,1),NTT(b,len,1); 66 for (RG int i=0;i<len;++i) a[i]=1LL*a[i]*b[i]%rhl; NTT(a,len,-1); 67 for (RG int i=mid+1;i<=r;++i){ 68 f[i]+=a[i-l]; if (f[i]>=rhl) f[i]-=rhl; 69 } 70 solve(mid+1,r); return; 71 } 72 73 int main(){ 74 #ifndef ONLINE_JUDGE 75 freopen("sum.in","r",stdin); 76 freopen("sum.out","w",stdout); 77 #endif 78 n=gi(),pre(),solve(0,n); 79 for (RG int i=0;i<=n;++i) 80 ans=(ans+1LL*f[i]*fac[i])%rhl; 81 printf("%d\n",ans); return 0; 82 }