模板分治FFT

Posted y2823774827y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板分治FFT相关的知识,希望对你有一定的参考价值。

蒟蒻写题解实在不易

前置

方法一:\\(Cdq+NTT\\)
方法二:多项式求逆
NTT总结多项式求逆总结

方法一

\\(Cdq+NTT\\)
\\[f_i=\\sum\\limits_{j=1}^i f_{i-j}g_j\\]
乍一看直接\\(cdq\\),然后发现树状数组类的东西好像做不了:\\[[l,mid]\\longrightarrow [mid+1,r]:w_x=\\sum\\limits_{i=l}^{mid(x)} f_i g_{x-i}\\]

直接上卷积就行,\\(O(nlog^2n)\\)

毒瘤的代码时间,由于我们是只把利用到的位置进行\\(NTT\\),才保证了复杂度

期间多个下标混合利用,细节很多,调了很久,建议先把思路完全理清再写代码

方法二

多项式求逆:
\\[g_0=0\\therefore \\sum\\limits_{j=1}^i f_{i-j}g_j=\\sum\\limits_{j=0}^i f_{i-j}g_j\\]

构造生成函数:\\(F(x)\\in f,G(x)\\in g\\)

则:\\[F(x)G(x)+1=F(x)\\longrightarrow F(x)\\equiv \\frac{-1}{G(x)-1}(mod~x^n)\\]

直接多项式求逆,\\(O(nlogn)\\)

Code(方法一)

#include<bits/stdc++.h>
typedef long long LL;
const LL mod=998244353,gg=3,maxn=1e6+9;
inline LL Read(){
    LL x(0),f(1); char c=getchar();
    while(c<'0' || c>'9'){
        if(c=='-') f=-1; c=getchar();
    }
    while(c>='0' && c<='9'){
        x=(x<<3)+(x<<1)+c-'0'; c=getchar();
    }
    return x*f;
}
inline LL Pow(LL base,LL b){
    LL ret(1);
    while(b){
        if(b&1) ret=ret*base%mod; base=base*base%mod; b>>=1;
    }return ret;
}
LL r[maxn];
inline void NTT(LL *a,LL n,LL type){
    for(LL i=0;i<n;++i) if(i<r[i]) std::swap(a[i],a[r[i]]);
    for(LL mid=1;mid<n;mid<<=1){
        LL wn(Pow(gg,(mod-1)/(mid<<1)));
        if(type==-1) wn=Pow(wn,mod-2);
        for(LL R=mid<<1,j=0;j<n;j+=R)
            for(LL k=0,w=1;k<mid;++k,w=w*wn%mod){
                LL x(a[j+k]),y(w*a[j+mid+k]%mod);
                a[j+k]=(x+y)%mod; a[j+mid+k]=(x-y+mod)%mod;
            }
    }
    if(type==-1){
        LL ty(Pow(n,mod-2));
        for(LL i=0;i<n;++i) a[i]=a[i]*ty%mod;
    }
}
inline LL Fir(LL n){
    LL limit(1),len(0);
    while(limit<n){
        limit<<=1; ++len;
    }
    for(LL i=0;i<limit;++i) r[i]=(r[i>>1]>>1)|((i&1)<<len-1);
    return limit;
}
LL n;
LL f[maxn],g[maxn],F[maxn],G[maxn],W[maxn];
inline void Solve(LL l,LL r){
    if(l==r) return;
    LL mid(l+r>>1);
    Solve(l,mid);
    for(LL i=l,x=0;i<=mid;++i,++x) F[x]=f[i];
    for(LL i=1,x=0;i<=r-l;++i,++x) G[x]=g[i];

    LL limit(Fir(r-l+mid-l+1));
    for(LL i=mid-l+1;i<limit;++i) F[i]=0;
    for(LL i=r-l-1+1;i<limit;++i) G[i]=0;
    NTT(F,limit,1); NTT(G,limit,1);
    for(LL i=0;i<limit;++i) W[i]=F[i]*G[i]%mod;
    NTT(W,limit,-1);

    for(LL i=mid+1,x=mid-l;i<=r;++i,++x) f[i]=(f[i]+W[x])%mod;
    Solve(mid+1,r);
}
int main(){
    n=Read();
    for(LL i=1;i<n;++i) g[i]=Read();
    f[0]=1;
    Solve(0,n-1);
    for(LL i=0;i<n;++i) printf("%lld ",f[i]);
    return 0;
}

以上是关于模板分治FFT的主要内容,如果未能解决你的问题,请参考以下文章

模板分治 FFT

分治FFT模板

Luogu4721 模板分治 FFT

[模板] 多项式: 乘法/求逆/分治fft/微积分/ln/exp/幂

洛谷 - P4721 模板分治 FFT(分治NTT)

分治FFT/NTT 模板