TJOI2016 & HEOI2016 求和 (NKOJ 4026) 题解
感谢Wikipedia提供数学公式。题面。
\begin{align} f(n)=\sum^n_{i=0}\sum^i_{j=0}S(i,j)\times2^i\times(j!) \end{align}
一个很直接的想法是去考虑把\(S(i,j)\)算出来,于是找到公式:
\begin{align} S(i,j)=\frac{1}{j!}\sum^j_{k=0}(-1)^{j-k}(^j_k)k^i=\sum^j_{k=0}\frac{(-1)^{j-k}k^i}{k!\times(j-k)!}\end{align}
注意到\(S(i,j)=0,\forall i<j\) ,所以
\begin{align} f(n)=\sum^n_{i=0}\sum^n_{j=0}S(i,j)\times2^j\times(j!)\end{align}
将 \(i=0\) 和 \(j=0\) 的情况提出来,那么
\begin{align} f(n)=1+\sum^n_{i=1}\sum^n_{j=1}S(i,j)\times2^j\times(j!)\end{align}
固定 \(j\) 求和,有
\begin{align} f(n)=1+\sum^n_{j=1}2^j\times(j!)\sum^n_{i=1}S(i,j)\end{align}
\begin{align} f(n)=1+\sum^n_{j=1}2^j\times(j!)\sum^n_{i=1}\sum^j_{k=0}\frac{(-1)^{j-k}k^i}{k!\times(j-k)!}\end{align}
再交换 \(k\) 与 \(i\) 求和的顺序
\begin{align} f(n)=1+\sum^n_{j=1}2^j\times(j!)\sum^j_{k=0}\frac{(-1)^{j-k}\sum^n_{i=1}k^i}{k!\times(j-k)!}\end{align}
观察右边的式子,稍做变换即是
\begin{align} \sum^j_{k=0}\frac{(-1)^{j-k}}{(j-k)!}\frac{\sum^n_{i=1}k^i}{k!}\end{align}
只需计算{\(\frac{(-1)^k}{k!}\)}与{\(\frac{\sum^n_{i=1}k^i}{k!}\)}的卷积,两个数列都可以在 \(O(n)\) 时间内处理出来(视 \(log(P)\) 为常数),卷积\(O(nlogn)\) ,再计算求和\(O(n)\),总时间复杂度 \(O(nlogn)\)。
Code:
#include<cstdlib> #include<cstring> #include<cstdio> #define swap(a,b) a^=b,b^=a,a^=b typedef long long ll; const int N=10000005; const ll P=998244353,g=3; ll ntt_gg[N]={1}; ll ksm(ll a,ll b) { ll o=1; if(a>=P) a%=P;if(b>=P-1) b%=P-1; for(;b;b>>=1,a=a*a%P) if(b&1) o=a*o%P;return o; } void NTT(ll A[],int n,int ty) { int i,j,k,m;ll t0,t1; for(i=0;i<n;i++) { for(j=0,k=i,m=1; m<n;m<<=1,k>>=1) j=(j<<1)|(k&1); if(i<j) swap(A[i],A[j]); } ntt_gg[0]=1; for(m=1;m<n;m<<=1) { t0=ksm(g,(P-1)+ty*(P-1)/(m<<1)); for(i=1;i<m;i++) ntt_gg[i]=ntt_gg[i-1]*t0%P; for(k=0;k<n;k+=m<<1) for(i=k;i<k+m;i++) { t0=A[i];t1=A[i+m]*ntt_gg[i-k]%P; A[i]=t0+t1;A[i]-=A[i]>=P?P:0; A[i+m]=t0-t1;A[i+m]+=A[i+m]<0?P:0; } } if(ty==1) return; t0=ksm(n,P-2); for(i=0;i<n;i++) A[i]=A[i]*t0%P; } ll A[N],B[N],C[N],D[N]; int n,i,j,k,l=1 ;ll s,d,ans; int main() { scanf("%d",&n); A[0]=A[1]=1;B[1]=n; for(i=2;i<=n;i++) A[i]=A[i-1]*ksm(i,P-2)%P; for(i=2;i<=n;i++) B[i]=ksm(i-1,P-2); for(i=2;i<=n;i++) B[i]=A[i-1]*(ksm(i,n)-1)%P*B[i]%P; for(i=1;i<=n;i++) if(i&1) A[i]=(P-1)*A[i]%P; while(l<=n) l<<=1;l<<=1; NTT(A,l,1);NTT(B,l,1); for(i=0;i<l;i++) A[i]=A[i]*B[i]%P; NTT(A,l,-1);s=d=ans=1; for(i=1;i<=n;i++) { s=s*i%P; d=(d<<1)%P; ans+=(s*d)%P*A[i]%P; ans-=ans>=P?P:0; } printf("%lld",ans); }