LP4721 分治 FFT

Author Avatar
空気浮遊 2019年02月22日
  • 在其它设备中阅读本文章

卡一点细节。

Solution

CDQ 有两种递归方式,就是如果右的答案需要从左获得,那可以是递归解决左,后解决中,再递归解决右的方式。这样可以保证每次递归到 $l=r$ 时 $l$ 的贡献被完全计算。

FFT 也可以是这种方式。对于本题有 $f_j=\sum \limits_{i=l}^{mid}f_ig_{j-i}$,每次我们可以将 $f$ 数组左移来使临时 FFT 的数组变得容易处理,左移后变成:$f_{j-l}=\sum \limits_{i=0}^{mid-l}f_ig_{j-i}$,因此像这样子来做 FFT 然后对右面计算额外的贡献就行了。

同时注意不能将 $f$ 的 $(mid, r]$ 的部分参与到 FFT 的计算中。

Code

// Code by ajcxsu
// Problem: cdqpoly

#include<bits/stdc++.h>
#define MOD (998244353)
using namespace std;

const int N=4e5+10;
int g[N], f[N], x[N], y[N], n, r[N], l;

int qpow(int x, int y, int mo) {
    int ret=1;
    while(y) {
        if(y&1) ret=1ll*ret*x%mo;
        x=1ll*x*x%mo, y>>=1;
    } return ret;
}

void dft(int x[], int d, int n) {
    for(int i=1;i<n;i++) if(i>r[i]) swap(x[i], x[r[i]]);
    int t, w, o;
    for(int i=1;i<n;i<<=1) {
        o=qpow(3, d*(MOD-1)/(i<<1)+MOD-1, MOD);
        for(int j=0;j<n;j+=(i<<1)) {
            w=1;
            for(int k=0; k<i; k++, w=1ll*w*o%MOD)
                t=1ll*x[i+j+k]*w%MOD, x[i+j+k]=(x[j+k]-t+MOD)%MOD,
                (x[j+k]+=t)%=MOD;
        }
    }
}

void mul(int a[], int b[], int m) {
    dft(a, 1, n), dft(b, 1, n); for(int i=0;i<n;i++) a[i]=1ll*a[i]*b[i]%MOD;
    dft(a, -1, n); int inv=qpow(n, MOD-2, MOD); for(int i=0;i<=m;i++) a[i]=1ll*a[i]*inv%MOD;
}

void solve(int l, int r) {
    if(l==r) return;
    int mid=(l+r)>>1;
    solve(l, mid);
    ::l=0; for(n=1; n<=2*(r-l+1); n<<=1) ::l++;
    for(int i=1;i<n;i++) ::r[i]=(::r[i>>1]>>1)|((i&1)<<(::l-1));
    fill(x, x+n, 0), fill(y, y+n, 0);
    copy(f+l, f+mid+1, x);
    copy(g, g+r-l+1, y);
    mul(x, y, r-l+1);
    for(int i=mid+1;i<=r;i++) f[i]=(f[i]+x[i-l])%MOD;
    solve(mid+1, r);
}

int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int n;
    cin>>n;
    f[0]=1;
    for(int i=1;i<n;i++) cin>>g[i];
    solve(0, n-1);
    for(int i=0;i<n;i++) cout<<f[i]<<" \n"[i==n-1];
    return 0;
}