「模板」多项式全家桶

泰勒展开

对于多项式复合函数 \(G(f)\),若已知其在 \(f_0\) 处的取值 \(G(f_0)\), 那么其在任意 \(f\) 处的取值 \(G(f)\) 可以表示为

\[G(f)=\sum_{n=0}^{\infty}\frac{G^{(n)}(f_0)}{n!}(f-f_0)^n \]

牛顿迭代

可以用于求解多项式方程。

给定多项式复合函数 \(G(x)\),求多项式 \(F(x)\operatorname{mod} x^N\) 使得 \(G(F(x))\equiv 0(\operatorname{mod} x^N)\)

考虑倍增法,假设已经求出 \(f_0\equiv f(\operatorname{mod} x^n)\)。在 \(f=f_0\) 处进行 \(\text{Taylor}\) 展开

\[G(f)=\sum_{n=0}^{\infty}\frac{G^{(n)}(f_0)}{n!}(f-f_0)^n \]

因为 \(f_0\equiv f(\operatorname{mod} x^n)\),所以 \((f_0-f)^2\equiv 0(\operatorname{mod} x^{2n})\),由此将上式化简为

\[G(f)\equiv G(f_0)+G'(f_0)(f-f_0) \quad(\operatorname{mod} x^{2n}) \]

要求解的 \(f\) 满足 \(G(f)\equiv 0(\operatorname{mod} x^{2n})\),因此

\[f(x)=f_0(x)-\frac{G(f_0(x))}{G'(f_0(x))}\quad (\operatorname{mod} x^{2n}) \]

如此迭代即可求得多项式 \(f\),称之为牛顿迭代法。


下面尝试用牛顿迭代解决一些常规的例子。

多项式求逆

已知 \(A(x)\),求 \(F(x)\) 满足 \(A(x)F(x)\equiv 1(\operatorname{mod} x^n)\)

构造 \(G(f)=\frac{1}{f}-A(x)\)\(G'(f)=-\frac{1}{f^2}\)

代入牛顿迭代式:

\[\begin{aligned} f(x)&\equiv f_0(x)-\frac{\frac{1}{f_0(x)}-A(x)}{-\frac{1}{f_0(x)^2}}\\ &\equiv f_0(x)(2-f_0(x)A(x)) \end{aligned} \]

可以 \(O(n\log n)\) 求出。


多项式对数函数

已知 \(F(x)\),求 \(G(x)\equiv \ln F(x)(\operatorname{mod} x^n)\)

\(\ln f=\int \operatorname{d}\ln f=\int f'f^{-1} \text{d}x\)

\(f\) 的导数和 \(f\) 的逆卷积,再进行积分即可。

时间复杂度 \(O(n\log n)\)

多项式指数函数

已知 \(A(x)\),求 \(F(x)\equiv e^{A(x)}(\operatorname{mod} x^n)\)

两边同时取 \(\ln\)\(\ln F(x)\equiv A(x)(\operatorname{mod} x^n)\)

构造 \(G(f)=\ln f-A(x)\)\(G'(f)=\frac{1}{f}\)

\[\begin{aligned} f(x)&\equiv f_0(x)-\frac{\ln f_0(x)-A(x)}{\frac{1}{f_0(x)}}\\ &\equiv f_0(x)(1-\ln f_0(x)+A(x)) \end{aligned} \]

时间复杂度 \(O(n\log n)\),但常数巨大。


多项式开根

已知 \(A(x)\),求 \(F(x)\equiv A(x)^{\frac{1}{2}}(\operatorname{mod} x^n)\)

根据 \(F(x)^2\equiv A(x)(\operatorname{mod} x^n)\),构造 \(G(f)=f^2-A(x)\)\(G'(f)=2f\)

\[\begin{aligned} f(x)&\equiv f_0(x)-\frac{f_0(x)^2-A(x)}{2f_0(x)}\\ &\equiv \frac{f_0(x)}{2}+\frac{A(x)}{2f_0(x)} \end{aligned} \]

时间复杂度 \(O(n\log n)\)


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> Poly;
const int N = 1 << 20, mod = 998244353, inv2 = mod + 1 >> 1;
int r[N], Inv[N];
inline int add(int x, int y) { return x + y >= mod ? x + y - mod : x + y; }
inline int dec(int x, int y) { return x - y < 0 ? x - y + mod : x - y; }
inline int mul(int x, int y) { return (ll)x * y % mod; }
inline int qpow(int a, int b) {
	int ans = 1;
	for (; b; b >>= 1, a = mul(a, a)) if (b & 1) ans = mul(ans, a);
	return ans;
}
inline void NTT(Poly &A, int len, int type){
	for (int i = 0; i < len; ++ i) if(i < r[i]) swap(A[i], A[r[i]]);
	for (int mid = 1; mid < len; mid <<= 1) {
		int Wn = qpow(type == 1 ? 3 : (mod + 1) / 3, (mod - 1) / (mid << 1));
		for (int j = 0; j < len; j += (mid << 1))
			for (int k = 0, w = 1; k < mid; ++ k, w = mul(w, Wn)) {
				int x = A[j + k], y = mul(w, A[j + k + mid]);
				A[j + k] = add(x, y), A[j + k + mid] = dec(x, y);
			}
	}
	if (type > 0) return;
	for (int i = 0; i < len; ++ i) A[i] = mul(A[i], Inv[len]);
}
inline void init_rev(int len) {
	for (int i = 0; i < len; ++ i) r[i] = r[i >> 1] >> 1 | ((i & 1) * (len >> 1));
}
inline Poly operator + (const Poly&a, const Poly&b) {
	Poly c = a; c.resize(max(a.size(), b.size()));
	for (int i = 0; i < b.size(); ++ i) c[i] = add(c[i], b[i]);
	return c;
}
inline Poly operator - (const Poly&a, const Poly&b) {
	Poly c = a; c.resize(max(a.size(), b.size()));
	for (int i = 0; i < b.size(); ++ i) c[i] = dec(c[i], b[i]);
	return c;
}
inline Poly operator * (Poly a, Poly b) {
	int n = a.size(), m = b.size(), l = 1;
	while (l < n + m - 1) l <<= 1;
	init_rev(l);
	a.resize(l), NTT(a, l, 1);
	b.resize(l), NTT(b, l, 1);
	for (int i = 0; i < l; ++ i) a[i] = mul(a[i], b[i]);
	NTT(a, l, -1);
	a.resize(n + m - 1);
	return a;
}
inline Poly operator * (Poly a, int b) {
	for (int i = 0; i < a.size(); ++ i) a[i] = mul(a[i], b);
	return a;
}
inline Poly Deriv(Poly a) {
	for (int i = 0; i + 1 < a.size(); ++ i) a[i] = mul(a[i + 1], i + 1);
	return a.pop_back(), a;
}
inline Poly Integ(Poly a) {
	a.emplace_back(0);
	for (int i = a.size() - 1; i; -- i) a[i] = mul(a[i - 1], Inv[i]);
	return a[0] = 0, a;
}
inline Poly Polyinv(const Poly&a, int lim) {
	Poly c, b(1, qpow(a[0], mod - 2));
	for (int l = 4; (l >> 2) < lim; l <<= 1) {
		init_rev(l);
		c = a, c.resize(l >> 1);
		c.resize(l), NTT(c, l, 1);
		b.resize(l), NTT(b, l, 1);
		for (int i = 0; i < l; ++ i) b[i] = mul(b[i], dec(2, mul(b[i], c[i])));
		NTT(b, l, -1), b.resize(l >> 1);
	}
	return b.resize(lim), b;
}
inline Poly Polyinv(const Poly&a) { return Polyinv(a, a.size()); }
inline Poly Polyln(Poly a, int lim) {
	a = Integ(Deriv(a) * Polyinv(a, lim));
	return a.resize(lim), a;
}
inline Poly Polyln(const Poly&a) { return Polyln(a, a.size()); }
inline Poly Polyexp(const Poly&a, int lim) {
	Poly c, b(1, 1); int n = a.size();
	for (int i = 2; (i >> 1) < lim; i <<= 1) {
		c = Polyln(b, i);
		for (int j = 0; j < i; ++ j) c[j] = dec(j < n ? a[j] : 0, c[j]);
		c[0] = add(c[0], 1);
		b = b * c, b.resize(i);
	}
	return b.resize(lim), b;
}
inline Poly Polyexp(const Poly&a) { return Polyexp(a, a.size()); }
inline Poly Polysqrt(const Poly&a, int lim) {
	Poly c, d, b(1, 1);
	for (int l = 4; (l >> 2) < lim; l <<= 1) {
		init_rev(l);
		c = a, c.resize(l >> 1);
		d = Polyinv(b, l >> 1);
		c.resize(l), NTT(c, l, 1);
		d.resize(l), NTT(d, l, 1);
		for (int j = 0; j < l; ++ j) c[j] = mul(c[j], d[j]);
		NTT(c, l, -1), b.resize(l >> 1);
		for (int j = 0; j < (l >> 1); ++ j) b[j] = mul(add(b[j], c[j]), inv2);
	}
	return b.resize(lim), b;
}
inline Poly Polysqrt(const Poly&a) { return Polysqrt(a, a.size()); }
inline Poly Polypow(Poly a, int k, int lim) {
	a = Polyexp(Polyln(a) * k);
	return a.resize(lim), a;
}
inline Poly Polypow(const Poly&a, int k) { return Polypow(a, k, a.size()); }
inline void precalc() {
	Inv[0] = Inv[1] = 1;
	for (int i = 2; i < N; ++ i) Inv[i] = mul(mod - mod / i, Inv[mod % i]);
}
int main() {
	precalc();
}
posted @ 2022-08-03 19:25  Samsara-soul  阅读(146)  评论(0编辑  收藏  举报