ACM多项式算法入门

在摸索了很久很久,还是没找到多项式的正确入门方式,所以尝试着自己写一下

内容包含

  • 多项式乘法
  • 多项式求逆
  • 多项式除法
  • 多项式取模
  • 多项式开根(待更新)
  • 多项式求ln(待更新)
  • 多项式求exp(待更新)

多项式乘法

  • 对于多项式 A ( x ) , B ( x ) A(x),B(x) A(x),B(x) A ( x ) = a n x n + a n − 1 x n − 1 + a n − 2 x n − 2 + a n − 3 x n − 3 + . . . + a 1 x + a 0 A(x)=a_nx^n + a_{n-1}x ^ {n-1} + a_{n-2}x ^ {n-2} + a_{n-3}x ^ {n-3} + ... + a_1x + a_0 A(x)=anxn+an1xn1+an2xn2+an3xn3+...+a1x+a0 B ( x ) = b n x n + b n − 1 x n − 1 + b n − 2 x n − 2 + b n − 3 x n − 3 + . . . + b 1 x + b 0 B(x)=b_nx^n + b_{n-1}x ^ {n-1} + b_{n-2}x ^ {n-2} + b_{n-3}x ^ {n-3} + ... + b_1x + b_0 B(x)=bnxn+bn1xn1+bn2xn2+bn3xn3+...+b1x+b0
    G ( x ) = A ( x ) B ( x ) G(x) = A(x)B(x) G(x)=A(x)B(x) 中第 x x x 项的系数 g x = ∑ i + j = x a i b j g_x=\sum_{i+j=x}a_ib_j gx=i+j=xaibj
    这是一个卷积的形式,因此我们可以考虑使用 F F T FFT FFT 或者 N T T NTT NTT 来优化这个卷积,这样就可以在 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n)) 时间内实现多项式乘法
void mul(LL* a,int n, LL* b,int m){
    for(int i=n+1; i<=Lim; i++) a[i] = 0;
    for(int i=m+1; i<=Lim; i++) b[i] = 0;
    for(int i=0; i<=n; i++) a[i] = (a[i]%P + P) % P; // 第一个多项式系数
    for(int i=0; i<=m; i++) b[i] = (b[i]%P + P) % P; // 第二个多项式系数
    NTT(a,1); NTT(b,1);
    for(int i=0; i<= Lim; i++) a[i] = a[i]*b[i] % P; // O(n)求点值表示法
    NTT(a,-1); // 逆变换变成系数表示法
}

多项式求逆

  • 定义

    • 对于多项式 A ( x ) A(x) A(x) 若存在多项式 B ( x ) B(x) B(x) 满足 A ( x ) B ( x ) = 1   m o d   ( x n ) A(x)B(x)=1 \bmod(x^n) A(x)B(x)=1mod(xn),那么称 B ( x ) B(x) B(x) A ( x ) A(x) A(x) 在模 x n x^n xn 意义下的逆
  • 倍增法

    • 利用了倍增的一种思想,当前需要求 A ( x ) A(x) A(x) 在模 x n x^n xn 下的逆,假设已知了 A ( x ) G ( x ) = 1   m o d   ( x ⌈ n 2 ⌉ ) (1) A(x)G(x)=1\bmod( x^{ \lceil \frac{n}{2} \rceil } ) \tag{1} A(x)G(x)=1mod(x2n)(1)那么我么现在需要求解 A ( x ) B ( x ) = 1   m o d   ( x n ) (2) A(x)B(x)=1 \bmod(x^n) \tag{2} A(x)B(x)=1mod(xn)(2)首先对于公式 ( 2 ) (2) (2)我们发现, A ( x ) B ( x ) A(x)B(x) A(x)B(x) 1 , 2... , n − 1 1,2...,n-1 1,2...,n1次项系数都是0,因此在 模 x ⌈ n 2 ⌉ x^{\lceil \frac{n}{2} \rceil} x2n的意义下一样成立, A ( x ) B ( x ) = 1   m o d   ( x ⌈ n 2 ⌉ ) (3) A(x)B(x)=1 \bmod(x^{ \lceil \frac{n}{2} \rceil }) \tag{3} A(x)B(x)=1mod(x2n)(3) ( 3 ) − ( 1 ) (3)-(1) (3)(1)我们可以得到 A ( x ) ( B ( x ) − G ( x ) ) = 0   m o d   ( x ⌈ n 2 ⌉ ) (4) A(x)(B(x)-G(x))=0\bmod(x^{ \lceil \frac{n}{2} \rceil }) \tag{4} A(x)(B(x)G(x))=0mod(x2n)(4)显然,这个A(x)不为零多项式(不然也没必要讨论他的逆了)那么就有 B ( x ) − G ( x ) = 0   m o d   ( x ⌈ n 2 ⌉ ) (5) B(x)-G(x)=0\bmod(x^{ \lceil \frac{n}{2} \rceil }) \tag{5} B(x)G(x)=0mod(x2n)(5)两边平方一下,注意到左边的多项式从 1 , 2... ⌈ n 2 ⌉ 1,2...\lceil \frac{n}{2} \rceil 1,2...2n 系数都是0,那么平方之后,前 n − 1 n-1 n1项系数都是0,因此平方之后在模 x n x^n xn的条件下成立。 B 2 ( x ) − 2 B ( x ) G ( x ) + G 2 ( x ) = 0   m o d   ( x n ) (6) B^2(x)-2B(x)G(x)+G^2(x)=0\bmod(x^n) \tag{6} B2(x)2B(x)G(x)+G2(x)=0mod(xn)(6)这个时候两边一起乘上 A ( x ) A(x) A(x) A ( x ) B 2 ( x ) − 2 A ( x ) B ( x ) G ( x ) + A ( x ) G 2 ( x ) = 0   m o d   ( x n ) (7) A(x)B^2(x)-2A(x)B(x)G(x)+A(x)G^2(x)=0\bmod(x^n) \tag{7} A(x)B2(x)2A(x)B(x)G(x)+A(x)G2(x)=0mod(xn)(7)利用公式 ( 2 ) (2) (2) 我们得到 B ( x ) − 2 G ( x ) + A ( x ) G 2 ( x ) = 0   m o d   ( x n ) (8) B(x)-2G(x)+A(x)G^2(x)=0\bmod(x^n) \tag{8} B(x)2G(x)+A(x)G2(x)=0mod(xn)(8) 移项之后,我么就得到了 A ( x ) A(x) A(x) 的逆 B ( x ) B(x) B(x) B ( x ) = 2 G ( x ) − A ( x ) G 2 ( x )   m o d   ( x n ) (9) B(x)=2G(x)-A(x)G^2(x)\bmod(x^n) \tag{9} B(x)=2G(x)A(x)G2(x)mod(xn)(9)这样我们就得到了多项式的递推方法
  • CODE

    // A的逆存在B中
    void get_inv(int n,LL *a,LL *b,LL mod){
        if (n == 1) { b[0] = qpow(a[0],mod-2); return;}
        get_inv((n + 1)/2,a,b,mod);
        int len = 0,lim = 1;
        while(lim < (n << 1)) lim <<= 1, ++len;
        for(int i=1; i<lim; i++) r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
        for(int i=0; i<n; i++) c[i] = a[i];
        for(int i=n; i<lim; i++) c[i] = 0;
        NTT(c,lim,1); NTT(b,lim,1);
        for(int i=0; i<lim; i++)
            b[i] = (2ll-c[i]*b[i]%mod+mod)%mod*b[i]%mod;
        NTT(b,lim,-1);
        for(int i=n; i<lim; i++) b[i] = 0;        
    }

多项式除法 - update at 2020.04.02

  • 学习完了多项式求逆,现在可以开始多项式除法了,先给一道例题[模板]多项式除法
  • 我们对于给定的 F ( x ) , G ( x ) F(x),G(x) F(x),G(x) F ( x ) = G ( x ) ∗ Q ( x ) + R ( x ) F(x)=G(x)*Q(x)+R(x) F(x)=G(x)Q(x)+R(x) 中的 Q ( x ) , R ( x ) Q(x),R(x) Q(x),R(x)
  • 首先考虑对于 F ( x ) = x n ∗ x − n F ( x ) F(x)=x^n*x^{-n}F(x) F(x)=xnxnF(x) ,令 x − n F ( x ) = F r ( x ) x^{-n}F(x)=Fr(x) xnF(x)=Fr(x),带入 并且令 x = 1 x x=\frac{1}{x} x=x1 F r ( x ) = x n ∗ F ( 1 x ) Fr(x)=x^n*F(\frac{1}{x}) Fr(x)=xnF(x1),对多项式有一定了解就会发现, F r ( x ) Fr(x) Fr(x) F ( x ) F(x) F(x) 的反转
  • 回到上述式子当中, F ( 1 x ) = G ( 1 x ) ∗ Q ( 1 x ) + R ( 1 x ) F(\frac{1}{x})=G(\frac{1}{x})*Q(\frac{1}{x}) + R(\frac{1}{x}) F(x1)=G(x1)Q(x1)+R(x1)那么两边乘 x n x^n xn x n F ( 1 x ) = x m G ( 1 x ) ∗ x n − m Q ( 1 x ) + x n − m + 1 ∗ x m − 1 R ( 1 x ) x^nF(\frac{1}{x})=x^mG(\frac{1}{x})*x^{n-m}Q(\frac{1}{x}) + x^{n-m+1}*x^{m-1}R(\frac{1}{x}) xnF(x1)=xmG(x1)xnmQ(x1)+xnm+1xm1R(x1)利用上述公式得到 F r ( x ) = G r ( x ) ∗ Q r ( x ) + x n − m + 1 ∗ x m − 1 R ( 1 x ) Fr(x)=Gr(x)*Qr(x)+x^{n-m+1}*x^{m-1}R(\frac{1}{x}) Fr(x)=Gr(x)Qr(x)+xnm+1xm1R(x1) x n − m + 1 x^{n-m+1} xnm+1取模得到 F r ( x ) = G r ( x ) ∗ Q r ( x )   m o d   ( x n − m + 1 ) Fr(x)=Gr(x)*Qr(x)\bmod (x^{n-m+1}) Fr(x)=Gr(x)Qr(x)mod(xnm+1) G r ( x ) Gr(x) Gr(x)求逆即可得到 Q r ( x ) = F r ( x ) ∗ G r − 1 ( x )   m o d   ( x n − m + 1 ) Qr(x)=Fr(x)*Gr^{-1}(x)\bmod (x^{n-m+1}) Qr(x)=Fr(x)Gr1(x)mod(xnm+1)反转即可得到 Q ( x ) Q(x) Q(x),最后再做一遍减法 R ( x ) = F ( x ) − Q ( x ) ∗ G ( x ) R(x)=F(x)-Q(x)*G(x) R(x)=F(x)Q(x)G(x)即可,乘法利用NTT优化,注意取模即可
#include 
using namespace std;
typedef long long LL;
inline long long read(){
   long long f = 1, x = 0;char ch = getchar();
    while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}
const int maxn = 4e5 + 10;
const LL mod = 998244353, G = 3;
LL f[maxn],g[maxn],fr[maxn],gr[maxn],q[maxn],rs[maxn];
namespace Poly{
    LL c[maxn],r[maxn],x[maxn];
    LL qpow(LL a,LL b){
        LL base = a % mod,ans = 1;
        while(b){
            if (b & 1) ans = ans*base % mod;
            base = base * base % mod;
            b >>= 1;
        }
        return ans % mod;
    }
    inline void NTT(LL *A,int Lim,int type){
        for(int i=0; i<Lim; i++) 
            if (i < r[i]) swap(A[i],A[r[i]]);
        for(int m = 1; m < Lim; m<<=1){
            LL wn = qpow(G, (mod-1)/(m << 1));
            for(int j=0; j<Lim; j+=(m<<1)){
                LL w = 1;
                for(int k=0; k<m; k++,w = w*wn % mod){
                    LL x = A[j+k],y = w*A[j+k+m] % mod;
                    A[j+k] = (x + y) % mod;
                    A[j+k+m] = (x - y + mod) % mod;
                }
            }
        }
        if (type == 1) return;
        LL inv = qpow(Lim,mod-2); reverse(A+1,A+Lim);
        for(int i=0; i<Lim; i++) A[i] = A[i]*inv % mod;
    }
    void init(){
        memset(c,0,sizeof(c));
        memset(r,0,sizeof(r));
    }
    void mul(LL* a, LL* b,int lim){
        memset(x,0,sizeof(x));
        for(int i=0; i<(lim >> 1); i++) x[i] = (b[i] % mod + mod) % mod;
        NTT(a, lim, 1); NTT(x, lim,1);
        for(int i=0; i<lim; i++) a[i] = a[i] * x[i] % mod;
        NTT(a, lim, -1);    
    }
    // A的逆存在B中
    void get_inv(int n,LL *a,LL *b,LL mod){
        if (n == 1) { b[0] = qpow(a[0],mod-2); return;}
        get_inv((n + 1)/2,a,b,mod);
        int len = 0,lim = 1;
        while(lim < (n << 1)) lim <<= 1, ++len;
        for(int i=1; i<lim; i++) r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
        for(int i=0; i<n; i++) c[i] = a[i];
        for(int i=n; i<lim; i++) c[i] = 0;
        NTT(c,lim,1); NTT(b,lim,1);
        for(int i=0; i<lim; i++)
            b[i] = (2ll-c[i]*b[i]%mod+mod)%mod*b[i]%mod;
        NTT(b,lim,-1);
        for(int i=n; i<lim; i++) b[i] = 0;        
    }
    // f(x) = q(x) * g(x) + r(x)
    void get_div(LL f[],int n,LL g[],int m,LL q[],LL rs[]){
        for(int i=0; i<=n; i++) fr[i] = f[n-i];
        for(int i=0; i<=m; i++) gr[i] = g[m-i];
        for(int i=n-m+2; i<=m; i++) gr[i] = 0;
        get_inv(n-m+1, gr, q, mod);
        int len = 0,lim = 1;
        while(lim <= (n << 1)) lim <<= 1, ++len;
        for(int i=1; i<=lim; i++) r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
        mul(q, fr, lim);
        reverse(q,q+n-m+1);
        for(int i=n-m+1; i<=n; i++) q[i] = 0;
        mul(g, q, lim);
        for(int i=0; i<=m; i++) rs[i] = (f[i] - g[i] + mod) % mod;
    }
}
int main(){
    int n = read(),m = read();
    for(int i=0; i<=n; i++) f[i] = read();
    for(int i=0; i<=m; i++) g[i] = read();
    Poly::get_div(f,n,g,m,q,rs);
    for(int i=0; i<=n-m; i++) printf("%d ",q[i]); printf("\n");
    for(int i=0; i<=m-1; i++) printf("%d ",rs[i]); printf("\n");
    return 0;
}   

待更新

多项式取模

多项式求ln

多项式求exp

你可能感兴趣的:(多项式,ACM,数学)