• 【模板】多项式除法


    问题描述

    给定一个 $n$次多项式 $F(x)$ 和一个 $m$ 次多项式 $G(x)$,请求出多项式 $Q(x)$,$R(x)$,满足以下条件:

    • $Q(x)$ 次数为 $n-m$,$R(x)$ 次数小于 $m$
    • $F(x) = Q(x) * G(x) + R(x)$

    所有运算在模998244353意义下进行

    详见洛谷 P4512

    分析

    具体来说,设多项式$A$为$n$次多项式,考虑一种操作$R$,使得

    $displaystyle A_R(x)=x^n A(frac{1}{x})$

    稍微想象一下,可以发现$A_R[i]=A[n-i]$($[i]$表示多项式的第$i$次系数)。

    这个操作可以$O(n)$完成。

    然后开始化式子。

    $$F(x)=Q(x) * G(x)+R(x)$$

    $$displaystyle F(frac{1}{x})=Q(frac{1}{x}) * G(frac{1}{x})+R(frac{1}{x})$$

    $$displaystyle x^n F(frac{1}{x})=x^{n-m} Q(frac{1}{x}) * x^m G(frac{1}{x})+x^{n-m+1} * x^{m-1} R(frac{1}{x})$$

    $$displaystyle F_R(x)=Q_R(x)*G_R(x)+x^{n-m+1} * R_R(x)$$

    $$displaystyle F_R(x) equiv Q_R(x)*G_R(x)pmod {x^{n-m+1}}$$

    $$displaystyle Q_R(x) equiv F_R(x)*G_R^{-1}(x)pmod {x^{n-m+1}}$$

    求一遍$G_R$的逆,然后就可以利用多项式乘法求出$Q$。然后

    $$R(x)=F(x)-G(x)*Q(x)$$

    直接计算即可。系数翻转可以用自带的 $reverse$ 函数,逆元最好迭代求解

    总的时间复杂度$O(nlogn)$。

    代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    const int N = 1<<20;
    
    int read()
    {
        int 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<<1) + (x<<3) + c - '0', c = getchar();
        return x * f;
    }
    namespace Polynomial
    {
        const ll P = 998244353, g = 3, gi = 332748118;
        static int rev[N];
        int lim, bit;
        ll add(ll a, ll b)
        {
            return (a += b) >= P ? a - P : a;
        }
        ll qpow(ll a, ll b)
        {
            ll prod = 1;
            while(b)
            {
                if(b & 1) prod = prod * a % P;
                a = a * a % P;
                b >>= 1;
            }
            return (prod + P) % P;
        }
        void calrev() {
            for(int i = 1; i < lim; i++)
                rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
        }
        void NTT(ll *A, int inv)
        {
            for(int i = 0; i < lim; i++)
                if(i < rev[i]) swap(A[i], A[rev[i]]);
            for(int mid = 1; mid < lim; mid <<= 1)
            {
                ll tmp = qpow(inv == 1 ? g : gi, (P - 1) / (mid << 1));
                for(int j = 0; j < lim; j += (mid << 1))
                {
                    ll omega = 1;
                    for(int k = 0; k < mid; k++, omega = (omega * tmp) % P) {
                        int x = A[j + k], y = omega * A[j + k + mid] % P;
                        A[j + k] = (x + y) % P;
                        A[j + k + mid] = (x - y + P) % P;
                    }
                }
            }
            if(inv == 1) return;
            int invn = qpow(lim, P - 2);
            for(int i = 0; i < lim; i++)
                A[i] = A[i] * invn % P;
        }
        static ll x[N], y[N];
        void mul(ll *a, ll *b)
        {
            memset(x, 0, sizeof x);
            memset(y, 0, sizeof y);
            for(int i = 0; i < (lim >> 1); i++)
                x[i] = a[i], y[i] = b[i];
            NTT(x, 1), NTT(y, 1);
            for(int i = 0; i < lim; i++)
                x[i] = x[i] * y[i] % P;
            NTT(x, -1);
            for(int i = 0; i < lim; i++)
                a[i] = x[i];
        }
        static ll c[2][N];
        void Inv(ll *a, int n)
        {
            int p = 0;
            memset(c, 0, sizeof c);
            c[0][0] = qpow(a[0], P - 2);
            lim = 2, bit = 1;
            while(lim <= (n << 1))
            {
                lim <<= 1, bit++;
                calrev();
                p ^= 1;
                memset(c[p], 0, sizeof c[p]);
                for(int i = 0; i <= lim; i++)
                    c[p][i] = add(c[p^1][i], c[p^1][i]);
                mul(c[p^1], c[p^1]);
                mul(c[p^1], a);
                for(int i = 0; i <= lim; i++)
                    c[p][i] = add(c[p][i], P - c[p^1][i]);
            }
            for(int i = 0; i < lim; i++)
                a[i] = c[p][i];
        }
    }
    using namespace Polynomial;
    int n,  m;
    ll F[N], G[N], Q[N], R[N], Gr[N];
    int main()
    {
        n = read(), m = read();
        for(int i = 0; i <= n; i++)
            F[i] = read(), Q[n - i] = F[i];
        for(int i = 0; i <= m; i++)
            G[i] = read(), Gr[m - i] = G[i];
        for(int i = n - m + 2; i <= m; i++)
            Gr[i] = 0;
        Inv(Gr, n - m + 1);    //Gr=Gr的逆
        mul(Q, Gr);            //Q=Q*Gr
        reverse(Q, Q + n - m + 1);   //Q=reverse(Q)
        for(int i = n - m + 1; i <= n; i++)
            Q[i] = 0;
        for(int i = 0; i <= n - m; i++)
            printf("%lld ", Q[i]);
        printf("
    ");
        lim = 1, bit = 0;
        while(lim <= (n << 2))
            lim <<= 1, bit++;
        calrev();
        mul(Q, G);
        for(int i = 0; i < m; i++)
            printf("%lld ", add(F[i], P - Q[i]));   //R=F - Q*G
        return 0;
    }

    代码转载自:https://www.luogu.org/blog/AKIOIorz/p4512-mu-ban-duo-xiang-shi-chu-fa

  • 相关阅读:
    Linux C 面试题总结
    linux下的缓存机制及清理buffer/cache/swap的方法梳理
    接入WebSocket记录 + 一些个人经验
    Linux基础系列—Linux体系结构和Linux内核结构
    typedef和define具体的详细区别
    RANSAC与 最小二乘(LS, Least Squares)拟合直线的效果比较
    深入理解C/C++混合编程优秀博文赏析与学习
    “error LNK2019: 无法解析的外部符号”之分析
    CUDA和OpenGL互操作经典博文赏析和学习
    [原创]C/C++语言中,如何在main.c或main.cpp中调用另一个.c文件
  • 原文地址:https://www.cnblogs.com/lfri/p/11236025.html
Copyright © 2020-2023  润新知