洛谷 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;
}
原文地址:https://www.cnblogs.com/yiqiAtiya/p/12070311.html