luogu P4721 【模板】分治 FFT
题面传送门
为什么写的是分治NTT啊,有哪位大佬会实数取模的FFT教我一下。
首先观察这个东西不是很好搞。因为后面要基于前面的转移。
但是我们发现如果用分治就可以无视这个限制。
所以用cdq分治,转移区间为\([l,r]\)中点\(mid\)就先把\(f\)的\([l,mid]\)和\(g\)的\([0,r-l]\)卷起来,然后转移到后面即可。
时间复杂度\(O(nlog^2n)\),常数还是蛮小的。
先把序列长度补成\(2\)的幂次会好写一点。
code:
#include<cstdio>
#define G 3
#define mod 998244353
#define ll long long
inline ll mpow(ll x,int y=mod-2){
ll ans=1;
while(y){if(y&1) ans=ans*x%mod;x=x*x%mod;y>>=1;}
return ans;
}
const ll invG=mpow(G);
using namespace std;
int n,m,k,x,y,z,tr[400039];
ll g[400039],f[400039],ans[400039],a[400039];
inline void make(int x){for(int i=0;i<x;i++) tr[i]=(tr[i>>1]>>1)|((i&1)?x>>1:0);}
inline void swap(ll &x,ll &y){x^=y^=x^=y;}
inline void ntt(ll *f,int n,int flag){
register int i,j,k;ll pus,key,now;
for(i=0;i<n;i++) if(tr[i]>i) swap(f[tr[i]],f[i]);
for(i=2;i<=n;i<<=1){
for(key=mpow(flag?G:invG,(mod-1)/i),j=0;j<n;j+=i){
for(pus=1,k=j;k<j+i/2;k++)now=f[k+i/2]*pus%mod,f[k+i/2]=(f[k]-now+mod)%mod,f[k]=(f[k]+now)%mod,pus=pus*key%mod;
}
}
}
inline void solve(int l,int r){
if(l==r){ans[l]=(ans[l]+a[l])%mod;return;}
int mid=l+r>>1,i,m=(r-l+1)*2;solve(l,mid);
for(i=0;i<m;i++) f[i]=g[i]=0;
for(i=l;i<=mid;i++)f[i-l]=ans[i];
for(i=0;i<r-l+1;i++)g[i]=a[i];make(m);
ntt(f,m,1);ntt(g,m,1);
for(i=0;i<m;i++) f[i]=f[i]*g[i]%mod;ntt(f,m,0);ll invn=mpow(m);
for(i=0;i<m;i++) f[i]=f[i]*invn%mod;
for(i=mid-l+1;i<r-l+1;i++) ans[l+i]=(ans[l+i]+f[i])%mod;solve(mid+1,r);
}
int main(){
freopen("1.in","r",stdin);
register int i;
scanf("%d",&n);
for(m=1;m<n;m<<=1);ans[0]=1;
for(i=1;i<n;i++)scanf("%lld",&a[i]);
solve(1,m);
for(i=0;i<n;i++)printf("%lld ",ans[i]);
}