再探多项式高端操作

你以为你以为的就是你以为的吗?

时间复杂度

所有多项式操作的时间复杂度都是 O ( n log ⁡ n ) O(n \log n) O(nlogn)的。
分析略。

参考时间测试(c++11,with O2, 1 0 5 10^5 105

乘法: ~50ms
求逆:~100ms
开根:~250ms
Exp:~400ms

多项式求逆

已知多项式 F ( x ) F(x) F(x),求 G ( x ) G(x) G(x)使得 F ( x ) × G ( x ) = 1 F(x)\times G(x)=1 F(x)×G(x)=1
注意到 G G G可逐位确定。 O ( n 2 ) O(n^2) O(n2)可暴力。
容易发现:

  1. 常数项为0的多项式没有逆,否则逆是唯一的。
  2. 一个有限多项式的逆有无穷多项。

举个例子:
( 1 − x ) × ( 1 + x + x 2 + . . . + x n + . . . ) = 1 (1-x)\times(1+x+x^2+...+x^n+...)=1 (1x)×(1+x+x2+...+xn+...)=1
这两个多项式互为逆。

求法

A n A_n An是在模 x n x^n xn意义下的多项式A。
G F n = 1 GF_n=1 GFn=1(在模 x n x^n xn下成立)
2 G F n − G 2 F n 2 = 1 2GF_n-G^2F_n^2=1 2GFnG2Fn2=1(此处平方,变成了在模 x 2 n x^{2n} x2n成立)
G ( 2 F n − G F n 2 ) = 1 G(2F_n-GF_n^2)=1 G(2FnGFn2)=1
因此可以倍增。
注意,中途的次数界要开到求逆次数的两倍。

多项式开方

F 2 ( x ) = G ( x ) F^2(x)=G(x) F2(x)=G(x),则F(x)是G(x)的开方。

  1. 对于次数不为0的G(x),F可能有无穷项。 x + 1 = x 2 + 2 x + 1 x+1=x^2+2x+1 x+1=x2+2x+1
  2. 模意义下可能无解,可能两解,可能一解。取决于常数项的二次剩余情况。

也可用

求法

F n = F 2 n F_n=F_{2n} Fn=F2n,在模x^n意义下
F n 2 + 2 F n F 2 n + F 2 n 2 = 0 F_n^2 + 2 F_nF_{2n}+F_{2n}^2=0 Fn2+2FnF2n+F2n2=0,在模x^2n意义下。
由于在模x^2n意义下 F 2 n = G F_{2n}=G F2n=G
F n 2 + 2 F n F 2 n + G = 0 F_n^2 + 2 F_nF_{2n}+G=0 Fn2+2FnF2n+G=0
即求得 F 2 n F_{2n} F2n

多项式Exp

若F(x)是常数项为0的指数型生成函数(EGF),则
定义: e F ( x ) = ∑ i ≥ 0 F i ( x ) / i ! e^F(x)=\sum_{i\geq0}F^i(x)/i! eF(x)=i0Fi(x)/i!
多项式的0次方被定义为1,因此 e F ( x ) e^F(x) eF(x)的常数项为1.

组合意义:对元素1…n进行集合划分, e F ( x ) e^F(x) eF(x)即为所有划分方案的egf。
具体地, [ x n / n ! ] [x^n/n!] [xn/n!]的系数即为将n个元素进行集合划分后的方案权和。
其中方案权和是指,一种集合划分方案可以被表示成集合的集合 A A A,则 权 ( A ) = ∏ B ∈ A [ x ∣ B ∣ ] F 权(A)=\prod_{B\in A} [x^{|B|}]F (A)=BA[xB]F

举个例子:若 F ( x ) F(x) F(x)是带标号无向连通图方案数的EGF,则 G ( x ) = e F ( x ) G(x)=e^{F(x)} G(x)=eF(x),其中 G ( x ) G(x) G(x)是无向简单图的方案数的EGF。

求法

即解方程 f ( x ) = l n ( x ) − A f(x)=ln(x)-A f(x)=ln(x)A
倍增求解,设已经求出了exp的前n项,设为x0. (初值:1=e^0)
f ( x ) f(x) f(x)在x0处泰勒展开,可以发现只有一开始两项是前2n项不全为0的。
所以 f ( x ) = f ( x 0 ) + f ′ ( x 0 ) ( x − x 0 ) = 0 f(x)=f(x0)+f'(x0)(x-x0)=0 f(x)=f(x0)+f(x0)(xx0)=0,解得 x = x 0 − f ( x 0 ) / f ′ ( x 0 ) x=x0-f(x0)/f'(x0) x=x0f(x0)/f(x0).
f ( x ) f(x) f(x)是个多项式对多项式的函数,因此 f ′ ( x ) = 1 / x f'(x)=1/x f(x)=1/x
最终结果是 x = x 0 ( 1 − ln ⁡ x 0 + A ) x=x0(1-\ln {x0}+A) x=x0(1lnx0+A).
ln ⁡ x 0 \ln x0 lnx0有无穷项,但是我们只需要2n项
注意,对于常数项不为0的多项式无法定义Exp.

多项式Ln

若G(x)为常数项为1的指数型生成函数,则
定义:若 e F ( x ) = G ( x ) e^{F(x)}=G(x) eF(x)=G(x),则 F ( x ) = ln ⁡ G ( x ) F(x)=\ln G(x) F(x)=lnG(x)
即为exp的逆运算。
运用这个操作,可以快速解决“连通图计数”一类问题。

求法

ln ⁡ ′ ( G ( x ) ) = G ′ ( x ) G ( x ) \ln'(G(x))=\frac {G'(x)} {G(x)} ln(G(x))=G(x)G(x)
求逆,乘法,积分即可。常数项默认为0.
至于为什么是对x求导,而不是对G(x)求导,此处作者囿于知识水平无法给出答案。
(我高中数学都还没学到导数.jpg)

注意,对于常数项不为1的多项式,不存在ln.

多项式幂

快速幂: O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)
对于常数项为1的多项式(也就是其ln存在的多项式),ln + exp: O ( n log ⁡ n ) O(n \log n) O(nlogn)
再探多项式高端操作_第1张图片

O(n^2)做多项式exp,ln的方法

假如你有一个多项式 T T T,你希望求出 T k = e x p ( k × l n ( T ) ) T^k=exp(k\times ln(T)) Tk=exp(k×ln(T))
我们已知这样一个关系:若 F ( x ) F(x) F(x)是带标号无向连通图方案数的EGF,则 G ( x ) = e F ( x ) G(x)=e^{F(x)} G(x)=eF(x),其中 G ( x ) G(x) G(x)是无向简单图的方案数的EGF。
那么,我们知道G和F对应的OGF的递推关系。为了求 e T e^T eT,先将T变成对应的OGF,然后做递推得到 e T e^T eT的OGF,然后再变回EGF即可。

	for(int i = 0; i <= n; i++) G[i] = G[i] * jc[i] % mo;
	for(int i = 1; i <= n; i++) {
		F[i] = G[i];
		for(int j = 1; j <= i - 1; j++) {
			F[i] -= C[i-1][j-1] * G[i - j] * F[j];
		}
	}
	for(int i = 0; i <= n; i++) F[i] = F[i] * 2;
	F[0] = 0;
	memset(G,0,sizeof G); G[0] = 1;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= i; j++) {
			G[i] += F[j] * C[i - 1][j - 1] * G[i - j];
		}
	}
	for(int i = 0; i <= n; i++) cout << G[i] * njc[i] % mo << " ";

多项式复合逆

若有两多项式 F , G F,G F,G,满足 F ( G ( x ) ) = x F(G(x))=x F(G(x))=x,则称其互为复合逆。
定理: F ( G ( x ) ) = G ( F ( x ) ) F(G(x))=G(F(x)) F(G(x))=G(F(x))
因此通常写作 ( G ⋅ F ) ( x ) (G\cdot F)(x) (GF)(x)
满足交换律与左右结合律。
相关链接:
拉格朗日反演及其扩展(这辈子都不会用到,笑)

多项式除法

待填

模板

实现丑陋,随便封装了一下。

typedef long long ll;
typedef vector<ll> poly;
const int N = 262244, mo = 998244353;
namespace Poly {
	ll w[N], Mx, M;
	ll jc[N], njc[N], inv[N];
	namespace PM {
		ll ksm(ll x, ll y) {
			ll ret = 1;
			for (; y; y >>= 1) {
				if (y & 1) ret = ret * x % mo;
				x = x * x % mo;
			}
			return ret;
		}

		void DFT(poly &a) {
			assert(a.size() <= M);
			a.resize(M);
			static int h[N];
			for(int i = 0; i < M; i++) {
				h[i] = (h[i >> 1] >> 1) + (i & 1) * (M >> 1);
				if(i < h[i]) swap(a[i], a[h[i]]);
			}
			for(int m = 1; m < M; m <<= 1) {
				int step = Mx / (m << 1);
				for(int i = 0; i < M; i += (m << 1)) {
					for(int j = 0; j < m; j++) {
						ll u = a[i + j + m] * w[step * j];
						a[i + j + m] = (a[i + j] - u) % mo;
						a[i + j] = (a[i + j] + u) % mo;
					}
				}
			}
		}

		void IDFT(poly &a) {
			reverse(a.begin() + 1, a.end());
			DFT(a);
			for(int i = 0; i < M; i++) a[i] = a[i] * inv[M] % mo;
		}

		poly operator * (poly a, poly b) {
			int sz = a.size() + b.size() - 1;
			for(M = 1; M < sz; M <<= 1);
			DFT(a), DFT(b);
			for(int i = 0; i < M; i++) a[i] = a[i] * b[i] % mo;
			IDFT(a);
			a.resize(sz);
			return a;
		}

		poly operator + (poly a, poly b) {
			int n = max(a.size(), b.size());
			a.resize(n), b.resize(n);
			for(int i = 0; i < n; i++) a[i] = (a[i] + b[i]) % mo;
			return a;
		}

		poly operator - (poly a, poly b) {
			int n = max(a.size(), b.size());
			a.resize(n), b.resize(n);
			for(int i = 0; i < n; i++) a[i] = (a[i] - b[i]) % mo;
			return a;
		}

		poly _Inv(poly a, int n) {
			a.resize(n);
			if (n == 1) {
				a[0] = ksm(a[0], mo - 2);
				return a;
			}
			poly b = _Inv(a, n >> 1);
			M = n * 2;
			DFT(a), DFT(b);
			for(int i = 0; i < M; i++) {
				a[i] = b[i] * (2 - a[i] * b[i] % mo) % mo;
			}
			IDFT(a);
			a.resize(n); return a;
		}

		//在模x^m意义下的逆元
		poly Inv(poly a, int n) {
			int Z; for(Z = 1; Z < n; Z <<= 1);
			a = _Inv(a, Z);
			a.resize(n); return a;
		}

		//求导
		poly Der(poly a) {
			for(int i = 0, n = a.size() - 1; i < n; i++) {
				a[i] = a[i + 1] * (i + 1) % mo;
			}
			a.pop_back();
			return a;
		}

		//积分
		poly Int(poly a) {
			a.push_back(0);
			for(int i = a.size() - 1; i; i--) {
				a[i] = a[i - 1] * inv[i] % mo;
			}
			a[0] = 0;
			return a;
		}

		poly Ln(poly a, int n) {
			a[0] = (a[0] + mo) % mo;
			assert(a[0] == 1);
			a.resize(n); a = Der(a) * Inv(a, n);
			a.resize(n); a = Int(a);
			a.resize(n); return a;
		}

		poly _Exp(poly a, int n) {
			a.resize(n);
			if (n == 1) {
				a[0] = 1; return a;
			}
			poly F0 = _Exp(a, n >> 1);
			poly b = a - Ln(F0, n); b[0] ++;
			F0 = F0 * b;
			F0.resize(n);
			return F0;
		}

		poly Exp(poly a, int n) {
			assert(a[0] == 0);
			int Z; for(Z = 1; Z < n; Z <<= 1);
			a = _Exp(a, Z);
			a.resize(n); return a;
		}

		poly Pow(poly x, int y) {
			x = Ln(x, x.size());
			for(int i = x.size() - 1; ~i; i--)
				x[i] = x[i] * y % mo;
			return Exp(x, x.size());
		}

		poly shift(poly a) {
			a.push_back(0);
			for(int i = a.size() - 1; i; i--)
				a[i] = a[i - 1];
			a[0] = 0;
			return a;
		}

		poly shiftLeft(poly a) {
			for(int i = 0; i < a.size(); i++)
				a[i] = a[i + 1];
			a.pop_back();
			return a;
		}
	}

	void init(int n) {
		for(Mx = 1; Mx <= 2 * n; Mx <<=1);
		w[0] = 1;
		w[1] = PM :: ksm(3, (mo - 1) / Mx);
		for(int i = 2; i < Mx; i++) w[i] = w[i - 1] * w[1] % mo;
		jc[0] = 1;
		for(int i = 1; i <= Mx; i++) jc[i] = jc[i - 1] * i % mo;
		njc[Mx] = PM :: ksm(jc[Mx], mo - 2);
		for(int i = Mx - 1; ~i; i--) njc[i] = njc[i + 1] * (i + 1) % mo;
		for(int i = 1; i <= Mx; i++) inv[i] = jc[i - 1] * njc[i] % mo;
	}
}

你可能感兴趣的:(新内容,多项式,FFT,求逆,Exp,Ln)