多项式求逆

多项式求逆

一些概念

多项式的度:对于一个多项式\(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;
}
posted @ 2019-01-09 12:10  ww3113306  阅读(284)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。