洛谷 P4721 分治FFT
因为要求出fj要先求出fi(i<j),且式子是和式,故fi对fj的影响是独立的,考虑运用CDQ分治的思想。
为了代码的高效优美简洁,有以下几个具体实现:
- 分治过程中用到STL函数(左闭右开),且区间长为2的幂,为方便,l,r,表示的应是区间[l,r)。
- 在区间[l,r)中,求出区间[l,mid)对区间[mid,r)的影响,而非[mid,Max)。
- 对于数组r,应在计算不同长度的卷积前重新计算(因为这个调了1小时)。
代码用NTT实现:
#include<bits/stdc++.h> #define IL inline #define LL long long using namespace std; const int N=4e5+3,P=998244353,G=3,Gi=332748118; IL int in(){ char c;int f=1; while((c=getchar())<'0'||c>'9') if(c=='-') f=-1; int x=c-'0'; while((c=getchar())>='0'&&c<='9') x=x*10+c-'0'; return x*f; } LL g[N],f[N],a[N],b[N]; int n,m,r[N],Max=1,cnt; IL LL ksm(LL a,LL b){ LL c=1; while(b){ if(b&1) c=c*a%P; a=a*a%P,b>>=1; } return c; } IL void get(int len){for(int i=0;i<len;++i) r[i]=(r[i>>1]>>1)|((i&1)*len>>1);} IL void FFT(LL *a,int len,int op){ for(int i=0;i<len;++i) if(i<r[i]) swap(a[i],a[r[i]]); for(int i=1;i<len;i<<=1){ LL wn=ksm(~op?G:Gi,(P-1)/(i<<1)); for(int j=0,l=i<<1;j<len;j+=l){ LL w=1; for(int k=0;k<i;++k,w=w*wn%P){ LL x=a[j+k],y=w*a[j+i+k]%P; a[j+k]=(x+y)%P,a[j+i+k]=(x-y+P)%P; } } } } void solve(int l,int r){ if(l+1==r) return; int mid=l+r>>1,m=(r-l)>>1; solve(l,mid); LL inv=ksm(r-l,P-2);get(r-l); memset(a+m,0,8*m),memcpy(a,f+l,8*m),memcpy(b,g,8*(r-l)); FFT(a,r-l,1),FFT(b,r-l,1); for(int i=0;i<r-l;++i) a[i]=a[i]*b[i]%P; FFT(a,r-l,-1); for(int i=m;i<r-l;++i) a[i]=a[i]*inv%P; for(int i=m;i<r-l;++i) f[l+i]=(f[l+i]+a[i])%P; solve(mid,r); } int main() { n=in(),f[0]=1; for(int i=1;i<n;++i) g[i]=in(); while(Max<n) Max<<=1,++cnt; solve(0,Max); for(int i=0;i<n;++i) printf("%lld ",f[i]); return 0; }