多 项 式 全 家 桶(便携式)

多项式卷积

\(F\times G\) 表示 \(F\)\(G\) 的卷积。暂时略去 FFT 与 NTT 的推导过程(应该……会补的吧?)。时间复杂度 \(O(n\log n)\)

多项式牛顿迭代

给定 \(F\)。求 \(G\) 使 \(F\circ G\equiv 0\pmod {x^n}\)。这里 \(F\circ G(x)=F(G(x))\)

考虑倍增。\(n=1\) 时单独求解。假设已经得到了 \(\bmod m_1=x^{\lceil\frac{n}{2}\rceil}\) 的解 \(G_1\),考虑 \(\bmod m=x^{n}\)

\(F\)\(G_1(x)\) 处进行泰勒展开,有

\[\sum_{i\ge 0}\frac{F^{(i)}(G_1(x))}{i!}(G(x)-G_1(x))^{i}\equiv 0\pmod {x^n} \]

因为 \(G(x)-G_1(x)\) 的最低非零项次数为 \(\lceil\frac{n}{2}\rceil\),所以 \(i\ge 2\) 时的项恒为 \(0\)。因此

\[F(G_1(x))+F'(G_1(x))(G(x)-G_1(x))\equiv 0\pmod {x^n}\\ G(x)=G_1(x)-\frac{F(G_1(x))}{F'(G_1(x))}\pmod {x^n} \]

一般来说不会直接用到,但是可以用它直接解决下面的几个问题。

多项式乘法逆

给定 \(F\),求 \(G\) 使 \(F\times G\equiv 1\pmod{x^{n}}\)\(G\) 可以记作 \(\frac{1}{F}\)\(F^{-1}\)

应用牛顿迭代。方程是 \(\frac{1}{G}-F=0\)。有 \(G=G_1-\frac{G_1^{-1}-F}{-G_1^{-2}}=2G_1-G_1^2F=G_1(2-G_1F)\)。时间复杂度 \(T(n)=T(\frac{n}{2})+O(n\log n)=O(n\log n)\)

多项式带余除法

给定 \(F,G\),求 \(Q,R\) 满足 \(F=G\times Q+R,\deg R\lt \deg G\)。可以证明 \((Q,R)\) 是唯一存在的。

\(F_r\) 表示将 \(F\) 的系数序列翻转之后的多项式,\(G_r,Q_r,R_r\) 同理。记 \(n=\deg F,m=\deg G\)。考虑将 \(\frac{1}{x}\) 作为自变量代入式子,则得到 \(F_r=G_r\times G_r+R\times x^{n-m+1}\)。式子两边对 \(x^{n-m+1}\) 取模得到 \(Q_r\equiv \frac{F_r}{G_r}\pmod {x^{n-m+1}}\)。而 \(\deg Q=n-m\),所以直接用多项式乘法计算 \(\frac{F_r}{G_r}\) 就可以得到 \(Q_r\)。有了 \(Q_r\) 自然有 \(Q\),那么 \(R=F-G\times Q\) 即可。时间复杂度 \(O(n\log n)\)

多项式开方

给定 \(F\),求 \(G\) 使 \(G^2\equiv F\pmod {x^n}\)

应用牛顿迭代。有 \(G=G_1-\frac{G_1^2-F}{2G_1}=\frac{F+G_1^2}{2G_1}\)。时间复杂度 \(O(n\log n)\)

多项式求导 / 不定积分

太简单了。直接按照定义做就可以了。时间复杂度 \(O(n)\)

多项式 ln

给定 \(F\),求 \(G\) 使 \(G\equiv \ln F\pmod {x^n}\)。注意只有当 \(F(0)=1\) 时有解。

\(\ln\) 没法处理,考虑两边求导。有 \(G'\equiv \frac{F'}{F} \pmod {x^n}\)。于是 \(G\equiv \int {\frac{F'}{F}} \pmod {x^n}\)

多项式 exp

给定 \(F\),求 \(G\) 使 \(G\equiv e^{F} \pmod {x^n}\)。注意只有当 \(F(0)=0\) 时有解。

应用牛顿迭代。有 \(G=G_1-\frac{\ln G_1-F}{\frac{1}{G_1}}=G_1(1-\ln G_1+F)\)。时间复杂度 \(O(n\log n)\)

多项式快速幂

给定 \(F,k\),求 \(F^k\bmod x^n\)

快速幂使用倍增?大可不必。直接做 \(e^{k\cdot \ln F}\) 即可。如果 \(F(0)\neq 1\) 则只要用 \(\frac{F}{F(0)}\) 计算,最后乘上 \(F(0)^{k}\)。时间复杂度 \(O(n\log n)\)。注意可能需要特判 \(F(0)=0\) 的情形。

其实直接使用倍增当然也是可以的,还能够处理任意的模多项式。

const int N=2e5+5,P=998244353;
int qpow(int a,int b=P-2){
    int c=1;
    for(;b;b>>=1,a=1ll*a*a%P)
        if(b&1)c=1ll*c*a%P;
    return c;
}
typedef vector<int> arr;
namespace poly{
    const int _G=3,invG=::qpow(3);
    int tr[N<<1];
    void NTT(arr&f,int op){
        int n=f.size();
        for(int i=0;i<n;++i)
            if(i<tr[i])swap(f[i],f[tr[i]]);
        for(int p=2;p<=n;p<<=1){
            int len=p>>1,tG=::qpow(op==1?_G:invG,(P-1)/p);
            for(int k=0;k<n;k+=p){
                int buf=1;
                for(int l=k;l<k+len;++l){
                    int tmp=1ll*buf*f[len+l]%P;
                    f[len+l]=(f[l]-tmp+P)%P;f[l]=(f[l]+tmp)%P;
                    buf=1ll*buf*tG%P;
                }
            }
        }
        if(op==-1){
            int inv=::qpow(n);
            for(int i=0;i<n;++i)f[i]=1ll*f[i]*inv%P;
        }
    }//NTT模板
    arr times(arr f,arr g){
        int n=f.size(),m=g.size();
        for(m+=n,n=1;n<=m;n<<=1);
        f.resize(n);g.resize(n);
        for(int i=0;i<n;++i)
            tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);
        NTT(f,1);NTT(g,1);
        for(int i=0;i<n;++i)f[i]=1ll*f[i]*g[i]%P;
        NTT(f,-1);return f;
    }//多项式卷积
    arr inv(int n,arr f){
        if(n==1)return {::qpow(f[0])};
        arr g=inv((n+1)/2,f);f.resize(n);
        f=times(f,g);for(int i=0;i<n;i++)f[i]=P-f[i];f[0]+=2;
        f.resize(n);f=times(f,g);f.resize(n);return f;
    }//多项式求逆
    arr sqrt(int n,arr f){
        if(n==1)return {1};//这里只考虑f[0]=1的情况。实际上可能要求二次剩余。
        arr g1=sqrt((n+1)/2,f);f.resize(n);
        arr g=times(g1,g1);g.resize(n);int inv2=(P+1)/2;
        for(int i=0;i<n;i++)g[i]=1ll*(f[i]+g[i])%P*inv2%P;
        g1.resize(n);g1=inv(n,g1);g=times(g,g1);g.resize(n);
        return g;
    }//多项式开根
    pair<arr,arr> mod(arr f,arr g){
        int n=f.size(),m=g.size();arr ff=f,gg=g;
        reverse(ff.begin(),ff.end());
        reverse(gg.begin(),gg.end());gg.resize(n+1);
        arr q=times(ff,inv(n+1,gg));
        q.resize(n-m+1);reverse(q.begin(),q.end());
        arr r=times(g,q);r.resize(m-1);
        for(int i=0;i<m-1;i++)r[i]=(f[i]-r[i]+P)%P;
        return make_pair(q,r);
    }//多项式带余除法
    arr ln(arr f){
        int n=f.size();arr f1;f1.resize(n);
        for(int i=1;i<n;i++)f1[i-1]=1ll*f[i]*i%P;
        f=inv(n,f);f1=times(f1,f);f1.resize(n);
        f[0]=0;for(int i=1;i<n;i++)f[i]=1ll*f1[i-1]*::qpow(i)%P;
        return f;
    }//多项式 ln
    arr exp(int n,arr f){
        if(n==1)return {1};
        arr g1=exp((n+1)/2,f);f.resize(n);g1.resize(n);
        arr g=ln(g1);for(int i=0;i<n;i++)g[i]=(f[i]-g[i]+P)%P;++g[0];
        g=times(g,g1);g.resize(n);return g;
    }//多项式 exp
    arr qpow(arr f,int k1,int k2){
        //因为k可能很大,所以传入k1=k%P,k2=k%(P-1)
        int n=f.size(),x=f[0],invx=::qpow(x);
        for(int i=0;i<n;i++)f[i]=1ll*f[i]*invx%P;
        f=ln(f);for(int i=0;i<n;i++)f[i]=1ll*f[i]*k1%P;
        f=exp(n,f);x=::qpow(x,k2);
        for(int i=0;i<n;i++)f[i]=1ll*f[i]*x%P;
        return f;
    }//多项式快速幂
}
posted @ 2024-03-27 11:22  by_chance  阅读(92)  评论(0编辑  收藏  举报