多项式——多项式除法

多项式——多项式除法

给定一个 n n n 次多项式 A ( x ) A(x) A(x) 和一个 m m m 次多项式 B ( x ) B(x) B(x) ,请求出多项式 D ( x ) D(x) D(x), R ( x ) R(x) R(x),满足以下条件:

  • D ( x ) D(x) D(x) 次数为 n − m n-m nm R ( x ) R(x) R(x) 次数小于 m m m
  • A ( x ) = D ( x ) B ( x ) + R ( x ) A(x) = D(x) B(x) + R(x) A(x)=D(x)B(x)+R(x)

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

P4512 【模板】多项式除法

多项式 A ( x ) A(x) A(x) 的从 a m a_m am a n a_n an 的系数可由下面的矩阵方程表示:

[ a n ⋮ a m + 1 a m ] = [ b m … 0 0 ⋮ ⋱ ⋮ ⋮ … … b m 0 … … b m − 1 b m ] [ d n − m ⋮ d 1 d 0 ] \begin{bmatrix} a_n \\ \vdots \\ a_{m+1} \\ a_{m} \end{bmatrix} = \begin{bmatrix} b_m & \dots & 0 & 0 \\ \vdots & \ddots & \vdots & \vdots \\ \dots & \dots & b_m & 0 \\ \dots & \dots & b_{m-1} & b_m \end{bmatrix} \begin{bmatrix}d_{n-m} \\ \vdots \\ d_1 \\ d_0\end{bmatrix} anam+1am = bm0bmbm100bm dnmd1d0

为了使我们计算方便,我们引入一种多项式转置运算:

A R ( x ) = x n A ( x − 1 ) = a n + a n − 1 x + ⋯ + a 0 x n A^R(x) = x^nA(x^{-1})= a_n + a_{n-1} x + \dots + a_0 x^n AR(x)=xnA(x1)=an+an1x++a0xn

B R ( x ) = x m B ( x − 1 ) = b m + b m − 1 x + ⋯ + b 0 x m B^R(x) = x^m B(x^{-1}) = b_m + b_{m-1} x + \dots + b_0 x^m BR(x)=xmB(x1)=bm+bm1x++b0xm

D R ( x ) = x n − m D ( x − 1 ) = d n − m + d n − m − 1 x + ⋯ + d 0 x n − m D^R(x) = x^{n-m}D(x^{-1}) = d_{n-m} + d_{n-m-1} x + \dots + d_0 x^{n-m} DR(x)=xnmD(x1)=dnm+dnm1x++d0xnm

那么这个线性系统就可以写为:

A R ( x ) ≡ B R ( x ) D R ( x ) m o d    x n − m + 1 A^R(x) \equiv B^R(x)D^R(x) \mod x^{n-m+1} AR(x)BR(x)DR(x)modxnm+1

根据多项式的乘法逆元,你可以无损的恢复 D ( x ) D(x) D(x) 的系数了:

D R ( x ) ≡ A R ( x ) ( B R ( x ) ) − 1 m o d    x n − m + 1 D^R(x) \equiv A^R(x) (B^R(x))^{-1} \mod x^{n-m+1} DR(x)AR(x)(BR(x))1modxnm+1

进而你可以恢复 R ( x ) = A ( x ) − B ( x ) D ( x ) R(x) = A(x) - B(x)D(x) R(x)=A(x)B(x)D(x)

请务必开 -O2 否则没有返回值优化。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

#ifdef LLT_DBG
#define FR freopen("in.txt", "r", stdin)
#else
#define FR
#endif

template <ll P>
ll fpow(ll a, ll b)
{
    ll res = 1;
    for (; b; b >>= 1, a = (a * a) % P)
        if (b & 1)
            res = (res * a) % P;
    return res;
}

template <ll G, ll P>
struct NTT
{
    int _n;
    int E;
    vector<int> rev;

    /**
     * @brief 构建一个 NTT 计算器
     *
     * @param n 多项式最高项数
     */
    NTT(int n)
    {
        _n = 1;
        E = 0;
        while (_n < n)
        {
            _n <<= 1;
            E++;
        }
        rev.resize(_n);
        // 逆位置对换
        for (int i = 1; i < _n; i++)
        {
            rev[i] = (rev[i >> 1] >> 1) + ((i & 1) << (E - 1));
        }
    }

    void _rNTT(vector<ll> &A, ll k)
    {
        for (int i = 0; i < _n; i++)
            if (i < rev[i])
                swap(A[i], A[rev[i]]);

        for (int e = 1; e <= E; e++)
        {
            int m = 1 << e;

            for (int i = 0; i < _n; i += m)
            {
                int hf = m / 2;
                ll g = 1;
                ll gn = fpow<P>(fpow<P>(G, (P - 1) / m), k);

                for (int j = 0; j < hf; j++)
                {
                    ll x = A[i + j];
                    ll y = (A[i + j + hf] * g) % P;
                    A[i + j] = (x + y) % P;
                    A[i + j + hf] = (x - y) % P;
                    g = (g * gn) % P;
                }
            }
        }
    }

    /**
     * @brief NTT 过程
     *
     * @param A 系数数组
     */
    void doNTT(vector<ll> &A)
    {
        _rNTT(A, 1);
    }

    /**
     * @brief NTT 逆过程
     *
     * @param A 点值数组
     */
    void doINTT(vector<ll> &A)
    {
        ll ni = fpow<P>(_n, P - 2);
        _rNTT(A, P - 2);
        for (int i = 0; i < _n; i++)
            A[i] = (A[i] * ni) % P;
    }
};

template <ll G, ll P>
struct Poly
{
    vector<ll> vec;
    int _n;

    Poly(int n) : vec(n), _n(n)
    {
    }
    Poly(ll an[], int n) : vec(n), _n(n)
    {
        for (int i = 0; i < n; i++)
            vec[i] = an[i];
    }

    void clip(int n)
    {
        vec.resize(n);
        _n = n;
    }

    Poly<G, P> operator+(const Poly<G, P> &o) const
    {
        Poly<G, P> p(max(_n, o._n));

        for (int i = 0; i < p._n; i++)
        {
            p.vec[i] = ((i < _n ? vec[i] : 0) + (i < o._n ? o.vec[i] : 0)) % P;
        }
        return p;
    }

    Poly<G, P> operator-(const Poly<G, P> &o) const
    {
        Poly<G, P> p(max(_n, o._n));

        for (int i = 0; i < p._n; i++)
        {
            p.vec[i] = ((i < _n ? vec[i] : 0) - (i < o._n ? o.vec[i] : 0)) % P;
        }

        return p;
    }

    Poly<G, P> operator*(ll v) const
    {
        Poly p(_n);

        for (int i = 0; i < _n; i++)
            p.vec[i] = (v * vec[i]) % P;

        return p;
    }

    Poly<G, P> operator*(const Poly<G, P> &o) const
    {
        NTT<G, P> ntt(_n + o._n - 1);
        Poly<G, P> p(ntt._n);

        vector<ll> expA(p._n);
        vector<ll> expB(p._n);

        for (int i = 0; i < _n; i++)
            expA[i] = vec[i];
        for (int i = 0; i < o._n; i++)
            expB[i] = o.vec[i];

        ntt.doNTT(expA);
        ntt.doNTT(expB);

        for (int i = 0; i < p._n; i++)
            p.vec[i] = (expA[i] * expB[i]) % P;

        ntt.doINTT(p.vec);

        p.clip(_n + o._n - 1);

        return p;
    }

    Poly<G, P> inv() const
    {
        Poly g(1);
        g.vec[0] = fpow<P>(vec[0], P - 2);

        for (int i = 1; i < _n;)
        {
            i <<= 1;
            Poly vk(i);
            for (int j = 0; j < i; j++)
                vk.vec[j] = (j < _n ? vec[j] : 0);
            g = g * 2 - g * g * vk;
            g.clip(i);
        }
        g.clip(_n);
        return g;
    }

    Poly<G, P> transpose() const
    {
        Poly p(_n);
        for (int i = 0; i < _n; i++)
            p.vec[i] = vec[i];
        reverse(p.vec.begin(), p.vec.end());
        return p;
    }

    pair<Poly<G, P>, Poly<G, P>> operator/(const Poly &o) const
    {
        Poly<G, P> ar = this->transpose();
        Poly<G, P> br = o.transpose();
        br.clip(_n - o._n + 1);
        Poly<G, P> dr = ar * br.inv();
        dr.clip(_n - o._n + 1);
        Poly<G, P> d = dr.transpose();
        Poly<G, P> r = (*this) - o * d;
        r.clip(o._n);

        return make_pair(d, r);
    }
};

ll A[100005];
ll B[100005];
const int mod = 998244353;

void solve()
{
    int n, m;
    scanf("%d %d", &n, &m);

    for (int i = 0; i <= n; i++)
    {
        scanf("%lld", A + i);
    }

    for (int i = 0; i <= m; i++)
    {
        scanf("%lld", B + i);
    }

    Poly<3, mod> ap(A, n + 1);
    Poly<3, mod> bp(B, m + 1);

    pair<Poly<3, mod>, Poly<3, mod>> dr = ap / bp;

    for (int i = 0; i < n - m + 1; i++)
    {
        printf("%lld ", (dr.first.vec[i] + mod) % mod);
    }

    printf("\n");

    for (int i = 0; i < m; i++)
    {
        printf("%lld ", (dr.second.vec[i] + mod) % mod);
    }
}

int main()
{
    FR;
    solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值