多项式求逆

多项式求逆

一些概念

多项式的度:对于一个多项式\(A(x)\),称其最高项为这个多项式的度,记为\(degA\)
多项式除法:对于多项式\(A(x), B(x)\),存在唯一确定的\(Q(x),R(x)\)满足\[A(x) = Q(x)B(x) + R(x)\]

其中\(degR< degB\).
那么称\(Q(x)\)\(B(x)\)\(A(x)\)的商,\(R(x)\)则为对应的余数,可记做:
\[A(x) \equiv R(x) \quad (mod \quad B(x))\]

多项式逆元:对于一个多项式\(A(x)\),如果存在\(B(x)\)满足\(degB \le degA\),且\(A(x)B(x) \equiv 1 \quad (mod \quad x^n)\),那么称\(B(x)\)\(A(x)\)\((mod \quad x^n)\)意义下的逆元,记作\(A^{-1}(x)\)

如何求解?

  • \(n = 1\)时,有\(A(x) \equiv c (mod \quad x)\),其中\(c\)是一个常数,所以\(A^{-1}(x) = c^{-1}\)
  • 对于\(n > 1\)的情况,设
    \[B(x) = A^{-1}(x)(mod \quad x^n), B'(x) = A^{-1}(x)(mod \quad x^{\lceil{\frac{n}{2}}\rceil})\]
    如果可以用\(B'\)\(A\)求出\(B\),那么我们就可以不断将求解\(B\)变为一个可以不断向下递归的子问题,而对于递归的终点:\(n = 1\),我们是可以通过直接计算得到答案的。
    因此我们来考虑如何用\(A(x)\)\(B'(x)\)表示\(B(x)\).

\(B(x),B'(x)\)的定义可得:
\[A(x)B(x) \equiv 1(mod \quad x^n) \Longrightarrow A(x)B(x) \equiv 1 (mod \quad x^{\lceil {\frac{n}{2}} \rceil})\]
\[A(x)B'(x) \equiv 1(mod \quad x^{\lceil {\frac{n}{2}} \rceil})\]
两式相减:
\[A(x)(B(x) - B'(x)) \equiv 0 (mod \quad x^{\lceil {\frac{n}{2}} \rceil})\]
\[B(x) - B'(x)\equiv 0(mod \quad x^{\lceil {\frac{n}{2}} \rceil})\]
两边平方得到:
\[B^2(x) - 2B'(x)B(x) + B'^2(x) \equiv 0 (mod \quad x^{\lceil {\frac{n}{2}} \rceil})\]

为什么模数也要平方?

左边多项式在\((mod \quad x^n)\)意义下为\(0\),说明其\(0\)\(n - 1\)次项系数均为0,平方后,对于第\(0 \le i \le 2n-1\)次项,有其系数\(b_i = \sum_{j = 0}^i a_ja_{i - j}\)
可以发现\(j\)\(i - j\)中,必然有一个值小于\(n\),因此\(b_i\)必然为\(0\).
所以平方后在\((mod \quad x^n)\)意义下仍为\(0\)

继续化式子:等式两边同时乘\(A(x)\),得到
\[B(x) - 2B'(x) + A(x)B'^2(x) \equiv 0(mod \quad x^n)\]
\[B(x) \equiv 2B'(x) - A(x)B'^2(x)(mod \quad x^n)\]
因此不断向上递推即可。

从上面的推导可以发现:一个多项式有无逆元取决于其常数项有无逆元。

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 1000000
#define LL long long
#define p 998244353

const int G = 3, Gi = 332748118;
int n, m, lim = 1, len;
int a[AC], b[AC], rev[AC], c[AC];

inline int read()
{
    int x = 0;char c = getchar();
    while(c > '9' || c < '0') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x;
}

LL qpow(LL x, int have)
{
    LL rnt = 1;
    while(have)
    {
        if(have & 1) rnt = rnt * x % p;
        x = x * x % p, have >>= 1;
    }
    return rnt;
}

void NTT(int *A, int opt)
{
    for(R i = 0; i < lim; i ++)
        if(i < rev[i]) swap(A[i], A[rev[i]]);
    for(R i = 1; i < lim; i <<= 1)
    {
        LL W = qpow((opt == 1) ? G : Gi, (p - 1) / (i << 1));
        for(R r = i << 1, j = 0; j < lim; j += r)
            for(R k = 0, w = 1; k < i; k ++, w = 1LL * w * W % p)
            {
                LL x = A[j + k], y = 1LL * w * A[j + k + i] % p;
                A[j + k] = (x + y) % p, A[j + k + i] = (x - y + p) % p;
            }
    }
}

void pre()
{
    n = read();
    for(R i = 0; i < n; i ++) a[i] = read();
}

void init(int n, int mid)
{
    lim = 1, len = 0;
    while(lim <= n) lim <<= 1, ++ len;//重新选定lim
    for(R i = 0; i < lim; i ++) //因为重新选了lim,所以rev数组也要更新
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (len - 1));  
    for(R i = 0; i < mid; i ++) c[i] = a[i];//取出要参与运算的 A里面的 前mid项
    for(R i = mid; i < lim; i ++) c[i] = 0;//后面清0,不然会对前面的运算有影响
}

void work(int n)//每次问题的规模都会减小一半,因为% x^k,显然大于等于k次的项都会被模掉
{
    if(n == 1) {b[0] = qpow(a[0], p - 2); return;}//只剩常数项了,求这个的逆元即可
    work((n + 1) >> 1); //先递归下去求解子问题
    init(n << 1, n);//2个长度为n的多项式相乘……,所以n + n = 2n
    NTT(c, 1), NTT(b, 1);
    for(R i = 0; i < lim; i ++)//计算当前层的B值
        b[i] = 1LL * b[i] * ((2LL - 1LL * c[i] * b[i] % p + p) % p) % p;
    NTT(b, -1);
    int inv = qpow(lim, p - 2);//把B数组还原
    for(R i = 0; i < n; i ++) b[i] = 1LL * b[i] * inv % p;
    for(R i = n; i < lim; i ++) b[i] = 0;//清空后面的项,防止对ans产生影响
}

int main()
{
    freopen("in.in", "r", stdin);
    pre();
    work(n);
    for(R i = 0; i < n; i ++) printf("%d ", b[i]);
    printf("\n");
    fclose(stdin);
    return 0;
}

转载于:https://www.cnblogs.com/ww3113306/p/10243554.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值