多项式半家桶

曾经有人说过自己这辈子不会遇到多项式科技,所以他没有学,直到模拟赛遇到了多项式开根。

本文将依托多项式乘法,介绍几种常见的多项式科技。因为笔者太菜,所以学的不多,以后可能会补充。

1 多项式乘法逆

1.1 过程

模板题:【模板】多项式乘法逆

我们现在要求出 \(F\) 在模 \(x^n\) 意义下的逆,考虑递归求解。假如我们当前已经求出了模 \(x^p\) 的逆,现在要推出模 \(x^{2p}\) 的逆。设 \(F\) 在模 \(x^p\) 下的逆元为 \(B'\),模 \(x^{2p}\) 的逆元为 \(B\),那么我们有:

\[\begin{aligned} &F\times B'\equiv 1\pmod {x^p}\\ &F\times B\equiv 1\pmod {x^{2p}} \end{aligned} \]

那么容易得到 \((B'-B)\equiv 0\pmod {x^p}\)。然后两边同时平方,注意模数的次数也要平方:

\[\begin{aligned} &(B'-B)^2\equiv 0\pmod {x^{2p}}\\ \Rightarrow &{B'}^2-2BB'+B^2\equiv 0\pmod {x^{2p}} \end{aligned} \]

然后左右两边同时乘 \(F\),这样 \(F\times B\) 就没了:

\[A{B'}^2-2B'+B\equiv 0\pmod {x^{2p}} \]

移项得:

\[B\equiv 2B'-A{B'}^2 \pmod {x^{2p}} \]

这样的话我们就得出了 \(B\) 的递推式,倍增处理即可。最后我们求出来的是模 \(x^{2^p}\) 的逆元,显然如果 \(2^p\ge n\) 那么它也是模 \(x^n\) 下的逆元,所以直接求出来的就是答案。

利用 NTT 计算多项式乘法,根据主定理,复杂度是 \(O(n\log n)\) 的。

1.2 代码

模板题代码如下:

#include <bits/stdc++.h>
#define il inline

using namespace std;

const int Maxn = (1 << 18) + 1;
const int Inf = 2e9;
const int Mod = 998244353;
const int YG = 3, InvG = 332748118;
il int Add(int x, int y) {return x + y >= Mod ? x + y - Mod: x + y;} il void pls(int &x, int y) {x = Add(x, y);}
il int Del(int x, int y) {return x - y < 0 ? x - y + Mod : x - y;} il void sub(int &x, int y) {x = Del(x, y);}
il int qpow(int a, int b, int P = Mod) {int res = 1; for(; b; a = 1ll * a * a % P, b >>= 1) if(b & 1) res = 1ll * res * a % P; return res;}
il int Inv(int a) {return qpow(a, Mod - 2);}
template <typename T> il void chkmax(T &x, T y) {x = (x >= y ? x : y);}
template <typename T> il void chkmin(T &x, T y) {x = (x <= y ? x : y);}
template <typename T>
il void read(T &x) {
	x = 0; char ch = getchar(); bool flg = 0;
	for(; ch < '0' || ch > '9'; ch = getchar()) flg = (ch == '-');
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
	flg ? x = -x : 0;
}
template <typename T>
il void write(T x, bool typ = 1) {
	static short Stk[50], Top = 0;
	x < 0 ? putchar('-'), x = -x : 0;
	do Stk[++Top] = x % 10, x /= 10; while(x);
	while(Top) putchar(Stk[Top--] | 48);
	typ ? putchar('\n') : putchar(' ');
}
il void IOS() {ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);}
il void File() {freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);}
bool Beg;

int n, F[Maxn], G[Maxn]; 
int lim = 1;
int r[Maxn];
il void NTT(int *a, int n, int typ) {
	for(int i = 0; i < n; i++) if(i < r[i]) swap(a[i], a[r[i]]);
	for(int h = 1; h < n; h <<= 1) {
		int cur = qpow(typ == 1 ? YG : InvG, (Mod - 1) / (h << 1));
		for(int i = 0; i < n; i += (h << 1)) {
			int w = 1;
			for(int j = 0; j < h; j++, w = 1ll * w * cur % Mod) {
				int x = a[i + j], y = 1ll * a[i + j + h] * w % Mod;
				a[i + j] = Add(x, y);
				a[i + j + h] = Del(x, y);
			}
		}
	}
	if(typ == -1) {
		int iv = Inv(n);
		for(int i = 0; i < n; i++) a[i] = 1ll * a[i] * iv % Mod;
	}
}

int A[Maxn], B[Maxn];
il void Inv(int *a, int *b, int n) {
	b[0] = Inv(a[0]);
	for(int lim = 2; lim <= n; lim <<= 1) {
		int len = lim << 1;
		for(int i = 0; i < lim; i++) A[i] = a[i], B[i] = b[i];
		for(int i = 0; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) * (len >> 1));
		NTT(A, len, 1), NTT(B, len, 1);
		for(int i = 0; i < len; i++)  b[i] = 1ll * B[i] * Del(2, 1ll * A[i] * B[i] % Mod) % Mod;
		NTT(b, len, -1);
		for(int i = lim; i < len; i++) b[i] = 0;
	}
}

bool End;
il void Usd() {cerr << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n"; }
int main() {
	read(n);
	for(int i = 0; i < n; i++) read(F[i]);
	while(lim <= n) lim <<= 1;
	Inv(F, G, lim);
	for(int i = 0; i < n; i++) write(G[i], 0);
    Usd();
	return 0;
}

2 多项式开根

2.1 过程

模板题:【模板】多项式开根

这个过程和多项式乘法逆非常相似,考虑递归求解。设 \(F\) 在模 \(x^p\) 下开根为 \(B'\),模 \(x^{2p}\) 下开根为 \(B\),那么我们有:

\[\begin{aligned} & {B'}^2\equiv F\pmod {x^p}\\ & {B}^2\equiv F\pmod {x^{2p}} \end{aligned} \]

那么容易得到 \((B'-B)\equiv 0\pmod {x^p}\)。然后两边同时平方,注意模数的次数也要平方:

\[\begin{aligned} &(B'-B)^2\equiv 0\pmod {x^{2p}}\\ \Rightarrow &{B'}^2-2BB'+B^2\equiv 0\pmod {x^{2p}} \end{aligned} \]

然后把平方项换掉:

\[{B'}^2-2BB'+F\equiv 0\pmod {x^{2p}} \]

移项得:

\[B\equiv \frac{{B'}^2+F}{2B'}\equiv\frac{B'}{2}+\frac{F}{B'} \pmod {x^{2p}} \]

这样的话我们就得出了 \(B\) 的递推式,用多项式求逆解决即可。根据主定理,复杂度是 \(O(n\log n)\) 的。

2.2 代码

#include <bits/stdc++.h>
#define il inline

using namespace std;

const int Maxn = (1 << 18) + 1;
const int Inf = 2e9;
const int Mod = 998244353;
const int YG = 3, InvG = 332748118, Inv2 = 499122177;
il int Add(int x, int y) {return x + y >= Mod ? x + y - Mod: x + y;} il void pls(int &x, int y) {x = Add(x, y);}
il int Del(int x, int y) {return x - y < 0 ? x - y + Mod : x - y;} il void sub(int &x, int y) {x = Del(x, y);}
il int qpow(int a, int b, int P = Mod) {int res = 1; for(; b; a = 1ll * a * a % P, b >>= 1) if(b & 1) res = 1ll * res * a % P; return res;}
il int Inv(int a) {return qpow(a, Mod - 2);}
template <typename T> il void chkmax(T &x, T y) {x = (x >= y ? x : y);}
template <typename T> il void chkmin(T &x, T y) {x = (x <= y ? x : y);}
template <typename T>
il void read(T &x) {
	x = 0; char ch = getchar(); bool flg = 0;
	for(; ch < '0' || ch > '9'; ch = getchar()) flg = (ch == '-');
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
	flg ? x = -x : 0;
}
template <typename T>
il void write(T x, bool typ = 1) {
	static short Stk[50], Top = 0;
	x < 0 ? putchar('-'), x = -x : 0;
	do Stk[++Top] = x % 10, x /= 10; while(x);
	while(Top) putchar(Stk[Top--] | 48);
	typ ? putchar('\n') : putchar(' ');
}
il void IOS() {ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);}
il void File() {freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);}
bool Beg;

int n, F[Maxn], G[Maxn]; 
int lim = 1;
int r[Maxn];
il void NTT(int *a, int n, int typ) {
	for(int i = 0; i < n; i++) if(i < r[i]) swap(a[i], a[r[i]]);
	for(int h = 1; h < n; h <<= 1) {
		int cur = qpow(typ == 1 ? YG : InvG, (Mod - 1) / (h << 1));
		for(int i = 0; i < n; i += (h << 1)) {
			int w = 1;
			for(int j = 0; j < h; j++, w = 1ll * w * cur % Mod) {
				int x = a[i + j], y = 1ll * a[i + j + h] * w % Mod;
				a[i + j] = Add(x, y);
				a[i + j + h] = Del(x, y);
			}
		}
	}
	if(typ == -1) {
		int iv = Inv(n);
		for(int i = 0; i < n; i++) a[i] = 1ll * a[i] * iv % Mod;
	}
}

int A[Maxn], B[Maxn];
il void Inv(int *a, int *b, int n) {
	b[0] = Inv(a[0]);
	for(int lim = 2; lim < (n << 1); lim <<= 1) {
		int len = lim << 1;
		for(int i = 0; i < lim; i++) A[i] = a[i], B[i] = b[i];
		for(int i = 0; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) * (len >> 1));
		NTT(A, len, 1), NTT(B, len, 1);
		for(int i = 0; i < len; i++)  b[i] = 1ll * B[i] * Del(2, 1ll * A[i] * B[i] % Mod) % Mod;
		NTT(b, len, -1);
		for(int i = lim; i < len; i++) b[i] = 0;
	}
	for(int i = 0; i <= (n << 1); i++) A[i] = B[i] = 0;
}

int C[Maxn], D[Maxn];
il void Sqrt(int *a, int *b, int n) {
	b[0] = 1;
	for(int lim = 2; lim < (n << 1); lim <<= 1) {
		int len = lim << 1;
		for(int i = 0; i < lim; i++) C[i] = a[i];
		Inv(b, D, lim);
		for(int i = 0; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) * (len >> 1));
		NTT(C, len, 1), NTT(D, len, 1);
		for(int i = 0; i < len; i++) C[i] = 1ll * C[i] * D[i] % Mod;
		NTT(C, len, -1);
		for(int i = 0; i < lim; i++) b[i] = 1ll * Add(b[i], C[i]) * Inv2 % Mod;
	}
	for(int i = 0; i <= (n << 1); i++) C[i] = D[i] = 0;
}

bool End;
il void Usd() {cerr << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n"; }
int main() {
	read(n);
	for(int i = 0; i < n; i++) read(F[i]);
	while(lim <= n) lim <<= 1;
	Sqrt(F, G, lim);
	for(int i = 0; i < n; i++) write(G[i], 0);
    Usd();
	return 0;
}
posted @ 2025-05-10 17:47  UKE_Automation  阅读(90)  评论(0)    收藏  举报