【学习笔记】多项式 3:多项式运算

Page Views Count

多项式求导与积分#

(axn)=a×nxn1

axndx=an+1xn+1

单独对每一项操作后加和。

预处理逆元,时间复杂度 O(n)

inline Poly Differntial(int n){
    Poly F;
    F.set(n);
    for(int i=1;i<n;++i) F[i-1]=f[i]*i%mod;
    return F;
} 
inline Poly Integral(int n){
    Poly F;
    F.set(n+1);
    for(int i=0;i<n;++i) F[i+1]=f[i]*inv[i+1]%mod;
    return F;
}

多项式牛顿迭代#

牛顿强啊!

设函数 G,要求一多项式 f(x),满足:

G(f(x))0(modxn)

假定已经求得 f0(x),满足:

G(f0(x))0(modxn/2)

泰勒展开:

G(f(x))=i=0G(i)(f0(x))i!(f(x)f0(x))i

由于右侧 f(x)f0(x) 的最低非零次项次数为 n/2,因此:

i2,(f(x)f0(x))i0(modxn)

于是式子实际上是:

G(f0(x))+G(f0(x))(f(x)f0(x))0(modxn)

整理一下就是:

f(x)f0(x)G(f0(x))G(f0(x))(modxn)

于是我们只需要求出 [x0]f(x) 再倍增即可。

多项式求逆#

给定 F(x),求 G(x) 使得:

F(x)G(x)1(modxn)

常规倍增法#

假设已经求得 G0 满足:

F(x)G0(x)1(modxn/2)

接下来推一下式子:

G(x)G0(x)(modxn/2)G(x)G0(x)0(modxn/2)(G(x)G0(x))20(modxn)F(x)(G2(x)2G(x)G0(x)+G02(x))0(modxn)G(x)2G0(x)+F(x)G02(x)0(modxn)

于是得到:

G(x)G0(x)(2F(x)G0(x))(modxn)

牛顿迭代法#

构造:

G(f(x))=1f(x)F(x)

代入牛顿迭代式:

f(x)=f0(x)1f0(x)F(x)1f02(x)

整理一下就是:

f(x)f0(x)(2F(x)f0(x))(modxn)

inline Poly Inv(int n){
    Poly F,G;
    G.set(1);
    G[0]=q_pow(f[0],mod-2,mod);
    for(int L=2;L<2*n;L<<=1){
        F.set(2*L),G.set(2*L);
        F.clear(0,2*L-1);
        for(int i=0;i<L;++i) F[i]=f[i];
        F.NTT(2*L,1),G.NTT(2*L,1);
        for(int i=0;i<2*L;++i) G[i]=1ll*(int)G[i]*(2ll-1ll*(int)G[i]*(int)F[i]%mod+mod)%mod;
        G.NTT(2*L,0);
        G.clear(min(n,L),2*L-1);
    }
    return G;
}

多项式开根#

给定 F(x),求 G(x) 使得:

F(x)G2(x)(modxn)

常规倍增法#

假设已经求得 G0 满足:

F(x)G02(x)(modxn/2)

接下来推一下式子:

G(x)G0(x)(modxn/2)G(x)G0(x)0(modxn/2)(G(x)G0(x))20(modxn)G2(x)2G(x)G0(x)+G02(x)0(modxn)F(x)2G(x)G0(x)+G02(x)0(modxn)

于是得到:

G(x)F(x)+G02(x)2G0(x)(modxn)

即:

G(x)F(x)2G0(x)+G0(x)2(modxn)

牛顿迭代法#

构造:

G(f(x))=f2(x)F(x)

代入牛顿迭代式:

f(x)=f0(x)f02(x)F(x)2f0(x)

整理一下就是:

f(x)F(x)+f02(x)2f0(x)(modxn)

即:

f(x)F(x)2f0(x)+f0(x)2(modxn)

inline Poly Sqrt(int n){
    Poly F,G,invG,H;
    G.set(1);
    G[0]=1;
    for(int L=2;L<2*n;L<<=1){
        F.set(2*L),G.set(2*L),H.set(2*L);
        F.clear(0,2*L-1);
        for(int i=0;i<L;++i) F[i]=f[i];
        invG=G.Inv(L);
        F.NTT(2*L,1),invG.NTT(2*L,1);
        for(int i=0;i<2*L;++i) H[i]=F[i]*invG[i]%mod*inv[2]%mod;
        H.NTT(2*L,0);
        for(int i=0;i<2*L;++i) G[i]=(H[i]+G[i]*inv[2]%mod)%mod;
        G.clear(min(n,L),2*L-1);
    }
    return G;
}

多项式对数函数#

给定 F(x),求 G(x) 使得:

lnF(x)G(x)(modxn)

左侧相当于一个复合函数,对左右求导:

(lnF(x))×F(x)G(x)(modxn)

根据简单初等函数的导数得:

F(x)F(x)G(x)(modxn)

求出 G(x) 后积分得到 G(x)

G(x)=G(x)dx

inline Poly Ln(int n){
    Poly dF,invF,G,H;
    dF=Differntial(n),invF=Inv(n);
    int L=1;
    while(L<2*n) L<<=1;
    dF.set(L),invF.set(L),G.set(L);
    dF.NTT(L,1),invF.NTT(L,1);
    for(int i=0;i<L;++i) G[i]=dF[i]*invF[i]%mod;
    G.NTT(L,0);
    H=G.Integral(L);
    H.clear(n,L);
    return H;
}

注意多项式牛顿迭代和多项式 ln 在求导时的区别,前者的 G(f(x)) 是关于 f(x) 的函数,也即 G(f(x))=dG(f(x))df(x);后者 G(x) 是关于 x 的函数,也即 G(x)=dG(x)dx,而左侧的 lnF(x) 是关于 x 的复合函数,所以要应用复合函数求导 (lnF(x))=dlnF(x)dF(x)×dF(x)dx

多项式指数函数#

给定 F(x),求 G(x) 使得:

expF(x)G(x)(modxn)

使用牛顿迭代法,构造:

G(f(x))=lnf(x)F(x)

代入牛顿迭代式:

f(x)=f0(x)lnf0(x)F(x)1f0(x)

整理一下就是:

f(x)f0(x)(1lnf0(x)+F(x))(modxn)

inline Poly Exp(int n){
    Poly F,G,lnG;
    G.set(1);
    G[0]=1;
    for(int L=2;L<2*n;L<<=1){
        F.set(2*L),G.set(2*L);
        F.clear(0,2*L-1);
        for(int i=0;i<L;++i) F[i]=f[i];
        lnG=G.Ln(L);
        F.NTT(2*L,1),G.NTT(2*L,1),lnG.NTT(2*L,1);
        for(int i=0;i<2*L;++i) G[i]=G[i]*(1ull-lnG[i]+F[i]+mod)%mod;
        G.NTT(2*L,0);
        G.clear(min(n,L),2*L-1);
    }
    return G;
}

多项式半家桶封装模板#

点击查看代码
inline int q_pow(int A,int B,int P){
    int res=1;
    while(B){
        if(B&1) res=1ll*res*A%P;
        A=1ll*A*A%P;
        B>>=1;
    }
    return res;
}

int inv[maxn];
int rev[maxn];
int base,w[maxn];
struct Poly{
    const static int g=3;
    int deg;
    vector<ull> f;
    ull &operator[](const int &i){return f[i];}
    ull operator[](const int &i)const{return f[i];}
    inline void set(int L){deg=L;f.resize(L);}
    inline void clear(int L,int R){for(int i=L;i<=R;++i)f[i]=0;}
    inline void output(int L){for(int i=0;i<L;++i)printf("%llu ",f[i]);printf("\n");}
    inline void NTT(int L,bool type){
        set(L);
        for(int i=1;i<L;++i){
            rev[i]=(rev[i>>1]>>1)+(i&1?L>>1:0);
            if(i<rev[i]) swap(f[i],f[rev[i]]);
        }
        for(int d=1;d<L;d<<=1){
            base=q_pow(type?g:q_pow(g,mod-2,mod),(mod-1)/(2*d),mod);
            w[0]=1;
            for(int i=1;i<d;++i) w[i]=1ll*w[i-1]*base%mod;
            for(int i=0;i<L;i+=d<<1){
                for(int j=0;j<d;++j){
                    ull x=f[i+j],y=f[i+d+j]*w[j]%mod;
                    f[i+j]=x+y,f[i+d+j]=x-y+mod;
                }
            }
        }
        for(int i=0;i<L;++i) f[i]%=mod;
        if(!type){
            int inv_L=q_pow(L,mod-2,mod);
            for(int i=0;i<L;++i) f[i]=f[i]*inv_L%mod;
        }
    }
    inline Poly Differntial(int n){
        Poly F;
        F.set(n);
        for(int i=1;i<n;++i) F[i-1]=f[i]*i%mod;
        return F;
    } 
    inline Poly Integral(int n){
        Poly F;
        F.set(n+1);
        for(int i=0;i<n;++i) F[i+1]=f[i]*inv[i+1]%mod;
        return F;
    }
    inline Poly Inv(int n){
        Poly F,G;
        G.set(1);
        G[0]=q_pow(f[0],mod-2,mod);
        for(int L=2;L<2*n;L<<=1){
            F.set(2*L),G.set(2*L);
            F.clear(0,2*L-1);
            for(int i=0;i<L;++i) F[i]=f[i];
            F.NTT(2*L,1),G.NTT(2*L,1);
            for(int i=0;i<2*L;++i) G[i]=1ll*(int)G[i]*(2ll-1ll*(int)G[i]*(int)F[i]%mod+mod)%mod;
            G.NTT(2*L,0);
            G.clear(min(n,L),2*L-1);
        }
        return G;
    }
    inline Poly Sqrt(int n){
        Poly F,G,invG,H;
        G.set(1);
        G[0]=1;
        for(int L=2;L<2*n;L<<=1){
            F.set(2*L),G.set(2*L),H.set(2*L);
            F.clear(0,2*L-1);
            for(int i=0;i<L;++i) F[i]=f[i];
            invG=G.Inv(L);
            F.NTT(2*L,1),invG.NTT(2*L,1);
            for(int i=0;i<2*L;++i) H[i]=F[i]*invG[i]%mod*inv[2]%mod;
            H.NTT(2*L,0);
            for(int i=0;i<2*L;++i) G[i]=(H[i]+G[i]*inv[2]%mod)%mod;
            G.clear(min(n,L),2*L-1);
        }
        return G;
    }
    inline Poly Ln(int n){
        Poly dF,invF,G,H;
        dF=Differntial(n),invF=Inv(n);
        int L=1;
        while(L<2*n) L<<=1;
        dF.set(L),invF.set(L),G.set(L);
        dF.NTT(L,1),invF.NTT(L,1);
        for(int i=0;i<L;++i) G[i]=dF[i]*invF[i]%mod;
        G.NTT(L,0);
        H=G.Integral(L);
        H.clear(n,L);
        return H;
    }
    inline Poly Exp(int n){
        Poly F,G,lnG;
        G.set(1);
        G[0]=1;
        for(int L=2;L<2*n;L<<=1){
            F.set(2*L),G.set(2*L);
            F.clear(0,2*L-1);
            for(int i=0;i<L;++i) F[i]=f[i];
            lnG=G.Ln(L);
            F.NTT(2*L,1),G.NTT(2*L,1),lnG.NTT(2*L,1);
            for(int i=0;i<2*L;++i) G[i]=G[i]*(1ull-lnG[i]+F[i]+mod)%mod;
            G.NTT(2*L,0);
            G.clear(min(n,L),2*L-1);
        }
        return G;
    }
};

参考资料#

  • OI Wiki

作者:SoyTony

出处:https://www.cnblogs.com/SoyTony/p/Learning_Notes_about_Polynomial_3.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   SoyTony  阅读(134)  评论(11编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示