Algorithm Notes 6 多项式

多项式

拉格朗日插值

  • 已知最高次数不超过 n − 1 n - 1 n1 的多项式在平面上的 n n n 个点 ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x n , y n ) (x_1,y_1),(x_2,y_2),\dots,(x_n,y_n) (x1,y1),(x2,y2),,(xn,yn),满足 ∀ 1 ≤ i < j ≤ n , x i ≠ x j \forall 1 \le i < j \le n, x_i\not=x_j ∀1i<jn,xi=xj,则可还原出多项式:
    f ( x ) = ∑ i = 1 n y i ∏ j ≠ i x − x j x i − x j f(x) = \sum\limits_{i = 1}^{n}y_i\prod\limits_{j \not=i} \frac{x - x_j}{x_i - x_j} f(x)=i=1nyij=ixixjxxj
  • x i = i x_i = i xi=i,且 x > n x > n x>n,则该式可简化为:
    f ( x ) = ( ∏ i = 1 n ( x − i ) ) ( ∑ i = 1 n ( − 1 ) n − i y i ( x − i ) ( i − 1 ) ! ( n − i ) ! ) f(x) = \left(\prod\limits_{i = 1}^{n}(x - i)\right)\left(\sum\limits_{i = 1}^{n}\frac{(-1)^{n-i}y_i}{(x-i)(i-1)!(n - i)!} \right) f(x)=(i=1n(xi))(i=1n(xi)(i1)!(ni)!(1)niyi)
  • 预处理相关逆元和前缀积,即可 O ( n ) \mathcal O(n) O(n) 计算。
  • 若已知二元多项式在空间内的 n m nm nm 个点 ( x 11 , y 11 , z 11 ) , … , ( x n m , y n m , z n m ) (x_{11},y_{11},z_{11}),\dots,(x_{nm},y_{nm},z_{nm}) (x11,y11,z11),,(xnm,ynm,znm),满足任意两点横纵坐标至少一个不相等,且该多项式中 x x x 的最高次数不超过 n − 1 n - 1 n1 y y y 的最高次数不超过 m − 1 m - 1 m1,则可还原出多项式:
    f ( x , y ) = ∑ i = 1 n ∑ j = 1 m z i j ∏ k ≠ i x − x k j x i j − x k j ∏ l ≠ j x − x i l x i j − x i l f(x,y) = \sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{m}z_{ij}\prod\limits_{k\not=i}\frac{x-x_{kj}}{x_{ij}-x_{kj}}\prod\limits_{l\not = j}\frac{x-x_{il}}{x_{ij}-x_{il}} f(x,y)=i=1nj=1mzijk=ixijxkjxxkjl=jxijxilxxil

FFT

  • 即快速傅里叶变换。
  • n n n 次单位根 ω n k = cos ⁡ 2 π k n + i sin ⁡ 2 π k n \omega_{n}^k = \cos\frac{2\pi k}{n} + i\sin\frac{2\pi k}{n} ωnk=cosn2πk+isinn2πk
  • 将多项式 A , B A,B A,B 的项数补到 2 的整数次幂,设项数为 n n n,采用分治法快速求出 A , B A,B A,B 代入 ω n 0 , ω n 1 , … , ω n n − 1 \omega_{n}^0, \omega^1_n, \dots, \omega^{n - 1}_{n} ωn0,ωn1,,ωnn1 的点值,将点值相乘再通过类似的过程还原回多项式,即可快速求出多项式 A × B A \times B A×B,时间复杂度 O ( n log ⁡ n ) \mathcal O(n \log n) O(nlogn)

DFT

  • 即离散傅里叶变换。
  • 对于多项式
    A ( x ) = ∑ k = 0 n − 1 a k x k = ∑ k = 0 n 2 − 1 ( a 2 k x 2 k + a 2 k + 1 x 2 k + 1 ) = ∑ k = 0 n 2 − 1 a 2 k ( x 2 ) k + x ∑ k = 0 n 2 − 1 a 2 k + 1 ( x 2 ) k A(x) = \sum\limits_{k = 0}^{n - 1}a_k x^k = \sum \limits_{k = 0}^{\frac{n}{2} - 1}(a_{2k}x^{2k} + a_{2k + 1}x^{2k + 1}) = \sum \limits_{k = 0}^{\frac{n}{2} - 1}a_{2k}(x^{2})^k + x\sum\limits_{k = 0}^{\frac{n}{2} - 1}a_{2k+1}(x^2)^k A(x)=k=0n1akxk=k=02n1(a2kx2k+a2k+1x2k+1)=k=02n1a2k(x2)k+xk=02n1a2k+1(x2)k
  • A 1 ( x ) = ∑ k = 0 n 2 − 1 a 2 k x k , A 2 ( x ) = ∑ i = 0 n 2 − 1 a 2 k + 1 x k A_1(x) = \sum \limits_{k = 0}^{\frac{n}{2} - 1}a_{2k}x^k, A_2(x) = \sum\limits_{i = 0}^{\frac{n}{2}-1}a_{2k + 1}x^k A1(x)=k=02n1a2kxk,A2(x)=i=02n1a2k+1xk,若已求得 A 1 ( ω n 2 k ) , A 2 ( ω n 2 k ) , 0 ≤ k ≤ n 2 − 1 A_1(\omega_\frac{n}{2}^k), A_2(\omega^{k}_{\frac{n}{2}}), 0 \le k \le \frac{n}{2} - 1 A1(ω2nk),A2(ω2nk),0k2n1,则
    A ( ω n k ) = A 1 ( ω n 2 k ) + w n k A 2 ( w n 2 k ) = A 1 ( ω n 2 k ) + w n k A 2 ( ω n 2 k ) A ( ω n k + n 2 ) = A 1 ( ω n 2 k ) − w n k + n 2 A 2 ( w n 2 k ) = A 1 ( ω n 2 k ) − w n k A 2 ( ω n 2 k ) A(\omega_n^k) = A_1(\omega^{2k}_{n}) + w_{n}^{k}A_2(w_{n}^{2k}) = A_1(\omega^{k}_{\frac{n}{2}}) + w_{n}^{k}A_2(\omega^{k}_{\frac{n}{2}}) \\ A(\omega_n^{k + \frac{n}{2}}) = A_1(\omega^{2k}_{n}) - w_{n}^{k+\frac{n}{2}}A_2(w_{n}^{2k}) = A_1(\omega^{k}_{\frac{n}{2}}) -w_{n}^{k}A_2(\omega^{k}_{\frac{n}{2}}) \\ A(ωnk)=A1(ωn2k)+wnkA2(wn2k)=A1(ω2nk)+wnkA2(ω2nk)A(ωnk+2n)=A1(ωn2k)wnk+2nA2(wn2k)=A1(ω2nk)wnkA2(ω2nk)
  • 不断分治下去,此时我们便求得了 A ( ω n k ) , 0 ≤ k ≤ n − 1 A(\omega^{k}_n), 0 \le k \le n - 1 A(ωnk),0kn1

IDFT

  • 即逆离散傅里叶变换,由之前的叙述我们有:
    [ ( ω n 0 ) 0 ( ω n 0 ) 1 ⋯ ( ω n 0 ) n − 1 ( ω n 1 ) 0 ( ω n 1 ) 1 ⋯ ( ω n 1 ) n − 1 ⋮ ⋮ ⋱ ⋮ ( ω n n − 1 ) 0 ( ω n n − 1 ) 1 ⋯ ( ω n n − 1 ) n − 1 ] [ a 0 a 1 ⋮ a n − 1 ] = [ A ( ω n 0 ) A ( ω n 1 ) ⋮ A ( ω n n − 1 ) ] \left[ \begin{matrix} (\omega_n^0)^0 & (\omega_n^0)^1 & \cdots & (\omega^0_n)^{n - 1}\\ (\omega_n^1)^0 & (\omega_n^1)^1 & \cdots & (\omega^1_n)^{n - 1}\\ \vdots & \vdots & \ddots & \vdots\\ (\omega_n^{n - 1})^0 & (\omega_n^{n-1})^1 & \cdots & (\omega^{n - 1}_n)^{n - 1}\\ \end{matrix} \right] \left[ \begin{matrix} a_0 \\ a_1 \\ \vdots \\ a_{n - 1} \\ \end{matrix} \right] = \left[ \begin{matrix} A(\omega_n^0)\\ A(\omega_n^1) \\ \vdots \\ A(\omega_n^{n - 1}) \\ \end{matrix} \right] (ωn0)0(ωn1)0(ωnn1)0(ωn0)1(ωn1)1(ωnn1)1(ωn0)n1(ωn1)n1(ωnn1)n1 a0a1an1 = A(ωn0)A(ωn1)A(ωnn1)
  • 对矩阵求逆,可得:
    [ a 0 a 1 ⋮ a n − 1 ] = 1 n [ ( ω n − 0 ) 0 ( ω n − 0 ) 1 ⋯ ( ω n − 0 ) n − 1 ( ω n − 1 ) ) 0 ( ω n − 1 ) 1 ⋯ ( ω n − 1 ) n − 1 ⋮ ⋮ ⋱ ⋮ ( ω n − ( n − 1 ) ) 0 ( ω n − ( n − 1 ) ) 1 ⋯ ( ω n − ( n − 1 ) ) n − 1 ] [ A ( ω n 0 ) A ( ω n 1 ) ⋮ A ( ω n n − 1 ) ] \left[ \begin{matrix} a_0 \\ a_1 \\ \vdots \\ a_{n - 1} \\ \end{matrix} \right] = \frac{1}{n} \left[ \begin{matrix} (\omega_n^{-0})^0 & (\omega_n^{-0})^1 & \cdots & (\omega^{-0}_n)^{n - 1}\\ (\omega_n^{-1}))^0 & (\omega_n^{-1})^1 & \cdots & (\omega^{-1}_n)^{n - 1}\\ \vdots & \vdots & \ddots & \vdots\\ (\omega_n^{-(n - 1)})^0 & (\omega_n^{-(n-1)})^1 & \cdots & (\omega^{-(n - 1)}_n)^{n - 1}\\ \end{matrix} \right] \left[ \begin{matrix} A(\omega_n^0)\\ A(\omega_n^1) \\ \vdots \\ A(\omega_n^{n - 1}) \\ \end{matrix} \right] a0a1an1 =n1 (ωn0)0(ωn1))0(ωn(n1))0(ωn0)1(ωn1)1(ωn(n1))1(ωn0)n1(ωn1)n1(ωn(n1))n1 A(ωn0)A(ωn1)A(ωnn1)
  • 不难发现,将 ω n k \omega_n^k ωnk 替换成 ω n − k \omega_n^{-k} ωnk 再做一遍 DFT,最后将结果除以 n n n 即可实现 IDFT

位逆序变换

  • 为了减小递归实现带来的常数,我们考虑直接得到递归最底层的排列顺序,再逐层向上合并。
  • 不难证明,设 n = 2 m n = 2^m n=2m,则系数 a i a_i ai 递归到最底层时的下标恰好为 i i i m m m 位二进制表示下的对称翻转,我们称这个变换为位逆序变换(蝴蝶变换)
  • r e v [ i ] rev[i] rev[i] 表示系数 a i a_i ai 递归到最底层时的下标,则显然有递推式
	int m = 0, _n = na + nb; //相乘的两个多项式最高次数分别为 na,nb
	for (n = 1; n <= _n; n <<= 1)
		++m;
	for (int i = 1; i < n; ++i)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (m - 1));
  • 最终我们得到了 FFT 的迭代实现。
typedef long double ld;
typedef complex<ld> com;
const ld pi = acos(-1.0);

inline void DFT(vector<com> &a, int opt)
{
	int n = a.size();
	for (int i = 0; i < n; ++i)
		if (i < rev[i])
			std::swap(a[i], a[rev[i]]);
	for (int k = 1; k < n; k <<= 1)
	{
		com w(cos(pi / k), opt * sin(pi / k));;
		for (int i = 0; i < n; i += k << 1)
		{
			com res(1.0, 0.0);
			for (int j = 0; j < k; ++j)
			{
				com u = a[i + j],
					v = res * a[i + j + k];
				a[i + j] = u + v;
				a[i + j + k] = u - v;
				res = res * w;
			}
		}
	}
	if (opt == -1)
	{
		for (int i = 0; i < n; ++i)
			a[i] /= n;
	}
}

NTT

  • 即快速数论变换。
  • 在模质数 P P P 意义下,原根 g g g 具有单位根的性质:
    ( g k ) P − 1 ≡ 1 ( m o d    P ) , g P − 1 2 ≡ − 1 ( m o d    P ) (g^k)^{P - 1} \equiv 1(\mod P), g^{\frac{P - 1}{2}}\equiv -1(\mod P) (gk)P11(modP),g2P11(modP)
  • P = 2 x a + 1 P = 2^xa +1 P=2xa+1,设 n = 2 m n = 2^m n=2m,用 g 2 x − m a g^{2^{x - m}a} g2xma 替换 ω n \omega_n ωn 即可实现 NTT
  • 常见的 P P P 有:
    P = 1004535809 = 479 × 2 21 + 1 , g = 3 P = 998244353 = 7 × 17 × 2 23 + 1 , g = 3 P = 1004535809 = 479 \times 2 ^ {21} + 1, g = 3 \\ P = 998244353 = 7 \times 17 \times 2^{23} + 1, g = 3 P=1004535809=479×221+1,g=3P=998244353=7×17×223+1,g=3
  • 实现时可以预处理 g 2 x − m a g^{2^{x - m}a} g2xma 的幂次,减小常数,代码见多项式基本操作。

多项式基本操作

  • 相关数组应开到题目给定长度的四倍,线性逆元应预处理到题目给定长度的两倍。

快速卷积的变式

  • 欲求 h k = ∑ i = 0 n − k f i g i + k h_k = \sum \limits_{i = 0}^{n - k}f_ig_{i+k} hk=i=0nkfigi+k,令 g n − i − k ′ = g i + k , h n − k ′ = h k g'_{n - i - k} = g_{i + k}, h'_{n - k} = h_k gnik=gi+k,hnk=hk,则原式可变为:
    h n − k ′ = ∑ i = 0 n − k f i g n − k − i ′ h'_{n - k} = \sum \limits_{i = 0}^{n - k}f_ig'_{n - k -i} hnk=i=0nkfignki
  • 做快速卷积即可。

分治NTT

  • 以计算 f i = ∑ j = 1 i f i − j g j ( 1 ≤ i < n ) f_i = \sum\limits_{j = 1}^{i}f_{i -j}g_j(1 \le i < n) fi=j=1ifijgj(1i<n) 为例, g 1 , g 2 , … , g n − 1 g_1, g_2, \dots, g_{n - 1} g1,g2,,gn1 已知, f 0 = 1 f_0 = 1 f0=1
  • 对于当前区间 [ l , r ] [l,r] [l,r],取中点 m i d mid mid
    • 先递归区间 [ l , m i d ] [l, mid] [l,mid]
    • g 1 , … , g r − l g_1, \dots, g_{r - l} g1,,grl f l , f l + 1 , … , f m i d f_{l}, f_{l + 1},\dots,f_{mid} fl,fl+1,,fmid 的卷积,求出两者对 f m i d + 1 , f m i d + 2 , … , f r f_{mid + 1}, f_{mid + 2}, \dots, f_r fmid+1,fmid+2,,fr 的贡献。
    • 再递归区间 [ m i d + 1 , r ] [mid + 1, r] [mid+1,r]
  • 时间复杂度 O ( n log ⁡ 2 n ) \mathcal O(n\log^2n) O(nlog2n)

牛顿迭代

  • 当前有一个函数 g g g,要求解出一个多项式 f f f 的前 n n n 项,使得 g ( f ) = 0 g(f) = 0 g(f)=0
  • 已知 f f f 的前 n n n f 0 f_0 f0,现在想求出 f f f 的前 2 n 2n 2n 项,由泰勒展开:
    0 ≡ g ( f 0 ) + g ′ ( f 0 ) ( f − f 0 ) ( m o d    x 2 n ) f ≡ f 0 − g ( f 0 ) g ′ ( f 0 ) ( m o d    x 2 n ) 0 \equiv g(f_0) + g'(f_0)(f - f_0) (\mod x^{2n}) \\ f \equiv f_0 - \frac{g(f_0)}{g'(f_0)}(\mod x^{2n}) \\ 0g(f0)+g(f0)(ff0)(modx2n)ff0g(f0)g(f0)(modx2n)

多项式求逆

  • 考虑和牛顿迭代类似的倍增法。
  • 设当前求出 B 0 ( x ) B_0(x) B0(x) 使得 A ( x ) B 0 ( x ) ≡ 1 ( m o d    x n ) A(x)B_0(x) \equiv 1 (\mod x^n) A(x)B0(x)1(modxn),则
    [ A ( x ) B 0 ( x ) − 1 ] 2 ≡ 0 ( m o d    x 2 n ) A ( x ) [ 2 B 0 ( x ) − A ( x ) B 0 2 ( x ) ] ≡ 1 ( m o d    x 2 n ) [A(x)B_0(x) - 1]^2 \equiv 0(\mod x^{2n}) \\ A(x)[2B_0(x) - A(x)B_0^2(x)] \equiv 1 (\mod x^{2n})\\ [A(x)B0(x)1]20(modx2n)A(x)[2B0(x)A(x)B02(x)]1(modx2n)
  • 不断迭代下去即可,时间复杂度 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn)

多项式取对数

  • ln ⁡ ( A ( x ) ) \ln(A(x)) ln(A(x)) 求导后再积分回来,则 B ( x ) = ∫ A ′ ( x ) A ( x ) d x B(x) = \int \frac{A'(x)}{A(x)} \text{d}x B(x)=A(x)A(x)dx
  • 需保证 A ( x ) A(x) A(x) 的常数项为 1。

多项式求指数

  • 套用牛顿迭代,令 g ( x ) = ln ⁡ ( x ) − A g(x) = \ln(x) - A g(x)=ln(x)A,最终要使 g ( B ) = 0 g(B) = 0 g(B)=0,代入上式,有
    B ( x ) ≡ B 0 ( x ) [ 1 − ln ⁡ B 0 ( x ) + A ( x ) ] ( m o d    x 2 n ) B(x) \equiv B_0(x) [1 - \ln B_0(x) + A(x)] (\mod x^{2n}) B(x)B0(x)[1lnB0(x)+A(x)](modx2n)
  • 需保证 A ( x ) A(x) A(x) 的常数项为 0。

多项式开根

  • 套用牛顿迭代,令 g ( x ) = x 2 − A g(x) = x^2 - A g(x)=x2A,最终要使 g ( B ) = 0 g(B) = 0 g(B)=0,代入上式,有
    B ( x ) ≡ B 0 ( x ) 2 + A ( x ) 2 B 0 ( x ) ( m o d    x 2 n ) B(x) \equiv \frac{B_0(x)^2 + A(x)}{2B_0(x)}(\mod x^{2n}) B(x)2B0(x)B0(x)2+A(x)(modx2n)
  • A ( x ) A(x) A(x) 的常数项不为 1,初始时求 B B B 的常数项需求二次剩余。

多项式快速幂

  • 朴素的算法即直接倍增,时间复杂度 O ( n log ⁡ 2 n ) \mathcal O(n\log^2 n) O(nlog2n)
  • 如果没有截断的情况,直接对点值求 k k k 次方也是正确的。
  • A ( x ) A(x) A(x) 的常数项为 1,即求 e k ln ⁡ A ( x ) e^{k\ln A(x)} eklnA(x) k k k 对模数 P P P 取模。
  • A ( x ) A(x) A(x) 的常数项不为 1,找到第一个系数非 0 的项,设为 a 0 = [ x t ] A ( x ) a_0 = [x^t]A(x) a0=[xt]A(x),则所求变为 a 0 k x t k ( A ( x ) a 0 x t ) k a_0^kx^{tk}\left(\frac{A(x)}{a_0x^t}\right)^k a0kxtk(a0xtA(x))k,乘以 a k a^k ak k k k φ ( P ) = P − 1 \varphi(P) = P - 1 φ(P)=P1 取模。
const int mod = 998244353;
const int inv2 = 499122177;
const int inv3 = 332748118;
int rev[N4], tw[N4], inv[N4]; //polyInt 中的 inv 要预处理(线性求逆元)

inline void operator += (vector<int> &a, vector<int> b)
{
	int n = b.size();
	a.resize(Max((int)a.size(), n));
	for (int i = 0; i < n; ++i)
		add(a[i], b[i]);
}

inline void operator -= (vector<int> &a, vector<int> b)
{
	int n = b.size();
	a.resize(Max((int)a.size(), n));
	for (int i = 0; i < n; ++i)
		dec(a[i], b[i]);
}

inline void operator *= (vector<int> &a, int k)
{
	if (k == -1)
	{
		int n = a.size();
		for (int i = 0; i < n; ++i)
			if (a[i])
				a[i] = mod - a[i];
	}
	else 
	{
		int n = a.size();
		for (int i = 0; i < n; ++i)
			a[i] = 1ll * k * a[i] % mod;
	}
}
 
inline void DFT(vector<int> &a, int opt)
{
	int n = a.size(), g = opt == 1 ? 3 : inv3;
	for (int i = 0; i < n; ++i)
		if (i < rev[i])
			std::swap(a[i], a[rev[i]]);
	for (int k = 1; k < n; k <<= 1)
	{
		int w = quick_pow(g, (mod - 1) / (k << 1));
		tw[0] = 1;
		for (int j = 1; j < k; ++j)
			tw[j] = 1ll * tw[j - 1] * w % mod;
		for (int i = 0; i < n; i += k << 1)
		{
			for (int j = 0; j < k; ++j)
			{
				int u = a[i + j],
					v = 1ll * tw[j] * a[i + j + k] % mod;
				add(a[i + j] = u, v);
				dec(a[i + j + k] = u, v);
			}
		}
	}
	if (opt == -1)
	{
		int inv_n = quick_pow(n, mod - 2); 
		for (int i = 0; i < n; ++i)
			a[i] = 1ll * a[i] * inv_n % mod;
	}
}

inline void polyMul(vector<int> &a, vector<int> b)
{
	if (!a.size() || !b.size())
	{
		a.clear();
		return ;
	}
	int m = 0, _n = a.size() + b.size() - 2, n;
	for (n = 1; n <= _n; n <<= 1)
		++m;
	a.resize(n);
	b.resize(n);
	for (int i = 1; i < n; ++i)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (m - 1));
	DFT(a, 1); DFT(b, 1);
	for (int i = 0; i < n; ++i)
		a[i] = 1ll * a[i] * b[i] % mod;
	DFT(a, -1);
	a.resize(_n + 1);
}

inline void solve(int l, int r)
{
	if (l == r)
		return ;
	int mid = l + r >> 1;
	solve(l, mid);
	vector<int> a(mid - l + 1), b(r - l + 1);
	for (int i = l; i <= mid; ++i)
		a[i - l] = f[i];
	for (int i = 0; i < r - l + 1; ++i)
		b[i] = g[i];
	polyMul(a, b);
	for (int i = mid + 1; i <= r; ++i)
		add(f[i], a[i - l]);
	solve(mid + 1, r);
}

inline void polyDer(vector<int> &a)
{
	int n = a.size();
	for (int i = 0; i < n; ++i)
		a[i] = 1ll * (i + 1) * a[i + 1] % mod;
	a.pop_back();
}

inline void polyInt(vector<int> &a)
{
	int n = a.size();
	a.push_back(0);
	for (int i = n - 1; i >= 0; --i)
		a[i + 1] = 1ll * inv[i + 1] * a[i] % mod;
	a[0] = 0;
}

//以下各函数 vector<int> &b 在传入之前需保证为空 

inline void polyInv(vector<int> a, vector<int> &b)
{
	int n = a.size();
	b.push_back(quick_pow(a[0], mod - 2));
	vector<int> c;
	int m = 1, m2, k;
	while (m < n)
	{
		m <<= 1, k = 0;
		for (m2 = 1; m2 < (m << 1); m2 <<= 1)
			++k;
		for (int i = 1; i < m2; ++i)
			rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (k - 1));
		b.resize(m2); c.resize(m2);
		for (int i = 0; i < m2; ++i)
			c[i] = i < n && i < m ? a[i] : 0; 
		DFT(b, 1); DFT(c, 1);
		for (int i = 0; i < m2; ++i)
		{
			int tmp = b[i];
			add(b[i], tmp);
			dec(b[i], 1ll * c[i] * tmp % mod * tmp % mod);
		}
		DFT(b, -1); 
		b.resize(m);
	}
	b.resize(n);
}

/*
polyInv's example:
5
1 6 3 4 9
1 998244347 33 998244169 1020

polyLn's example:
6
1 927384623 878326372 3882 273455637 998233543
0 927384623 817976920 427326948 149643566 610586717 

polyExp's example:
reversed
*/

inline void polyLn(vector<int> a, vector<int> &b)
{
	int n = a.size();
	polyInv(a, b);
	polyDer(a);
	polyMul(b, a);
	b.resize(n - 1);
	polyInt(b);
}

inline void polyExp(vector<int> a, vector<int> &b)
{
	int n = a.size();
	b.push_back(1);
	vector<int> c;
	int m = 1;
	while (m < n)
	{
		m <<= 1; 
		c.clear();
		b.resize(m);
		polyLn(b, c);
		for (int i = 0; i < m; ++i)
			add(c[i] = mod - c[i], i < n ? a[i] : 0);
		add(c[0], 1);
		polyMul(b, c);
		b.resize(m);
	}
	b.resize(n);
}

// 调用该函数前应先特判因前若干项为 0 导致结果全为 0 的情况 
// k1 是指数对 mod 取模后的值, k2 是指数对 mod - 1 取模后的值
// 指数较小时两者可合并
 
inline void polyPow(vector<int> &a, int k1, int k2)
{     
	int n = a.size(), st = 0;
	while (st < n && !a[st])
		++st;
	for (int i = st; i < n; ++i)
		a[i - st] = a[i];
	for (int i = n - st; i < n; ++i)
		a[i] = 0;
	a.resize(n);
	
	int a0 = a[0], inv_a0 = quick_pow(a0, mod - 2);
	for (int i = 0; i < n; ++i)
		a[i] = 1ll * a[i] * inv_a0 % mod;
	vector<int> b; 
	polyLn(a, b);
	for (int i = 0; i < n; ++i)
		b[i] = 1ll * b[i] * k1 % mod;
	a.clear();
	polyExp(b, a);
	
	a.resize(n);
	a0 = quick_pow(a0, k2);
	for (int i = 0; i < n; ++i)
		a[i] = 1ll * a[i] * a0 % mod;
	for (int i = n - 1; i >= 0; --i)
		if (i + 1ll * st * k1 < n)
			a[i + 1ll * st * k1] = a[i];
	for (int i = 0, im = Min((ll)n, 1ll * st * k1); i < im; ++i)
		a[i] = 0;
}

inline void polySqrt(vector<int> a, vector<int> &b)
{
	int n = a.size();
	b.push_back(1);
	vector<int> c, d; 
	int m = 1;
	while (m < n)
	{
		m <<= 1;
		b.resize(m);
		c.clear();
		polyInv(b, c);
		d.resize(m);
		for (int i = 0; i < m; ++i)
			d[i] = i < n ? a[i] : 0;
		polyMul(c, d);
		for (int i = 0; i < m; ++i)
			b[i] = 1ll * inv2 * (c[i] + b[i]) % mod;  
	}
	b.resize(n);
}

生成函数

OGF

  • 即一般生成函数 A ( x ) = ∑ i ≥ 0 a i x i A(x) = \sum \limits_{i \ge 0}a_ix^i A(x)=i0aixi
  • 常用公式: 1 1 − x = ∑ i ≥ 0 x i , 1 ( 1 − x ) 2 = ∑ i ≥ 0 ( i + 1 ) x i , 1 ( 1 − a x ) m = ∑ i ≥ 0 ( i + m − 1 m − 1 ) a i x i \frac{1}{1 - x} = \sum \limits_{i\ge 0}x^i, \frac{1}{(1-x)^2} = \sum \limits_{i \ge 0}(i + 1)x^i,\frac{1}{(1 - ax)^m} = \sum \limits_{i\ge 0}\binom{i + m - 1}{m - 1}a^ix^i 1x1=i0xi,(1x)21=i0(i+1)xi,(1ax)m1=i0(m1i+m1)aixi
  • 推导上述公式尽量用消项法或组合意义,求导/积分则较为麻烦。
  • 结论 1 1 − y ( y 1 − y ) k = ∑ n ≥ 0 ( n k ) y n \dfrac{1}{1 - y}\left(\dfrac{y}{1 - y}\right)^k = \sum \limits_{n \ge 0}\binom{n}{k}y^n 1y1(1yy)k=n0(kn)yn

证明
A ( x , y ) = ∑ k ≥ 0 ( ∑ n ≥ 0 ( n k ) y n ) x k = ∑ n ≥ 0 ∑ k ≥ 0 ( n k ) x k y n = ∑ n ≥ 0 [ ( 1 + x ) y ] n = 1 1 − y − x y = 1 1 − y 1 1 − y 1 − y x = ∑ k ≥ 0 1 1 − y ( y 1 − y ) k x k \begin{aligned} A(x, y) &= \sum \limits_{k \ge 0} \left(\sum \limits_{n \ge 0}\binom{n}{k}y^n\right) x^k\\ &= \sum \limits_{n \ge 0}\sum \limits_{k \ge 0} \binom{n}{k} x^k y^n \\ &= \sum \limits_{n \ge 0} [(1 + x)y]^n \\ &= \dfrac{1}{1 - y - xy}\\ &= \dfrac{1}{1 - y} \dfrac{1}{1 - \dfrac{y}{1 - y}x}\\ &= \sum \limits_{k\ge 0}\dfrac{1}{1 - y}\left(\dfrac{y}{1 - y}\right)^kx^k\\ \end{aligned} A(x,y)=k0(n0(kn)yn)xk=n0k0(kn)xkyn=n0[(1+x)y]n=1yxy1=1y111yyx1=k01y1(1yy)kxk

典例 Bobo String Count
题目大意
  • 给定一个 01 串 t t t,求在长度为 n n n t t t 恰好出现 k k k 次的 01 串个数,答案对 998244353 取模。
  • 0 ≤ k ≤ n 0 \le k \le n 0kn 1 ≤ n , ∣ t ∣ ≤ 5 × 1 0 4 1 \le n, |t| \le 5 \times 10^4 1n,t5×104
解法
  • m = ∣ t ∣ m = |t| m=t,设 g i g_i gi 表示长度为 i + m i + m i+m 的字符串长度为 m m m 的前后缀均为 t t t 的方案数,则:
    • i < m i < m i<m,若 m − i m - i mi t t t 的 border,则 g i g_i gi 为 1,否则为 0。
    • i ≥ m i \ge m im,则 g i = 2 i − m g_i = 2^{i - m} gi=2im
  • h i h_i hi 表示长度为 i + m i + m i+m 的字符串长度为 m m m 的前后缀均为 t t t 且中间不再有 t t t 出现的方案数,则根据容斥原理有:
    h i = g i − ∑ j = 1 i − 1 h j g i − j h_i = g_i - \sum\limits_{j = 1}^{i - 1}h_jg_{i - j} hi=gij=1i1hjgij
  • H ( x ) = ∑ i ≥ 1 h i x i H(x) = \sum\limits_{i \ge 1}h_ix^i H(x)=i1hixi G ( x ) = ∑ i ≥ 0 g i x i G(x) = \sum\limits_{i\ge 0}g_ix^i G(x)=i0gixi,则有:
    G = H G + 1 ⇔ H = 1 − 1 G G = HG + 1 \Leftrightarrow H = 1 - \dfrac{1}{G} G=HG+1H=1G1
  • p i p_i pi 表示长度为 i + m i + m i+m 的字符串前(后)缀为 t t t 的方案数,同样根据容斥原理有:
    p i = 2 i − ∑ j = 1 i h j 2 i − j p_i = 2^i - \sum \limits_{j = 1}^{i}h_j2^{i-j} pi=2ij=1ihj2ij
  • P ( x ) = ∑ i ≥ 0 p i x i P(x) = \sum \limits_{i \ge 0}p_ix^i P(x)=i0pixi,讨论 k k k
    • k = 0 k = 0 k=0,则答案为 2 n − ∑ j = 0 n − m p j 2 n − m − j 2^n - \sum \limits_{j = 0}^{n- m}p_j2^{n - m - j} 2nj=0nmpj2nmj,时间复杂度 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn)
    • k > 0 k > 0 k>0,则答案为 [ x n − m ] P 2 H k − 1 [x^{n - m}]P^2H^{k - 1} [xnm]P2Hk1,时间复杂度 O ( n log ⁡ n log ⁡ k ) \mathcal O(n \log n \log k) O(nlognlogk)

EGF

  • 即指数型生成函数 A ( x ) = ∑ i ≥ 0 a i i ! x i A(x) = \sum \limits_{i \ge 0}\frac{a_i}{i!}x^i A(x)=i0i!aixi

  • 若将 B B B 划分为若干个非空无序集合 A A A,则 B B B A A A E G F \mathbb{EGF} EGF 的关系为( A ( x ) A(x) A(x) 常数项必须为 0, B ( x ) B(x) B(x) 常数项必须为 1):
    B ( x ) = ∑ i ≥ 0 A ( x ) i i ! = e A ( x ) , A ( x ) = ln ⁡ B ( x ) B(x) = \sum \limits_{i \ge 0}\frac{A(x)^i}{i!} = e^{A(x)},A(x) = \ln B(x) B(x)=i0i!A(x)i=eA(x)A(x)=lnB(x)

    • 例如若 B B B 为排列, A A A 为置换,则
      B ( x ) = ∑ i ≥ 0 i ! i ! x i = ∑ i ≥ 0 x i = 1 1 − x A ( x ) = ∑ i ≥ 1 ( i − 1 ) ! i ! x i = ∑ i ≥ 1 x i i = ln ⁡ 1 1 − x B(x) = \sum \limits_{i \ge 0} \dfrac{i!}{i!}x^i = \sum \limits_{i \ge 0}x^i = \dfrac{1}{1 - x} \\ A(x) = \sum \limits_{i \ge 1} \dfrac{(i - 1)!}{i!}x^i = \sum \limits_{i \ge 1}\dfrac{x^i}{i} = \ln \dfrac{1}{1 - x} \\ B(x)=i0i!i!xi=i0xi=1x1A(x)=i1i!(i1)!xi=i1ixi=ln1x1
    • 常见的例子即可通过 n n n 个点有标号无向图的 E G F \mathbb{EGF} EGF ln ⁡ \ln ln 得到 n n n 个点有标号无向连通图的 E G F \mathbb{EGF} EGF,对 n n n 个点有标号无向树的 E G F \mathbb{EGF} EGF exp \text{exp} exp 即可得到 n n n 个点有标号无向森林的 E G F \mathbb{EGF} EGF
  • 常用公式(即 Taylor \text{Taylor} Taylor 级数):
    e x + e − x 2 = ∑ i ≥ 0 且 i 为偶数 x i i ! ,   e x − e − x 2 = ∑ i ≥ 0 且 i 为奇数 x i i ! ln ⁡ ( 1 + x ) = ∑ i ≥ 1 ( − 1 ) i + 1 i x i ,   ln ⁡ ( 1 − x ) = − ∑ i ≥ 1 x i i ( 1 + x ) α = 1 + ∑ i ≥ 1 ∏ j = α − i + 1 α j i ! x i \frac{e^x + e^{-x}}{2} = \sum \limits_{i \ge 0 且 i 为偶数} \frac{x^i}{i!},\ \frac{e^x-e^{-x}}{2} = \sum \limits_{i \ge 0 且 i 为奇数} \frac{x^i}{i!}\\ \ln (1 + x) = \sum\limits_{i\ge 1}\frac{(-1)^{i + 1}}{i}x^i, \ \ln(1 - x) = -\sum\limits_{i \ge 1}\frac{x^i}{i} \\ (1 + x)^{\alpha} = 1 + \sum \limits_{i \ge 1} \frac{\prod\limits_{j = \alpha - i +1}^{\alpha}j}{i!}x^i 2ex+ex=i0i为偶数i!xi, 2exex=i0i为奇数i!xiln(1+x)=i1i(1)i+1xi, ln(1x)=i1ixi(1+x)α=1+i1i!j=αi+1αjxi

典例1 CF891E
题目大意
  • n n n 个数 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an,要进行 k k k 次操作,每次随机选择一个数 x ∈ [ 1 , n ] x \in [1, n] x[1,n],把 a x a_x ax 减 1,将答案增加除 a x a_x ax 外所有数的乘积。
  • 求最终答案的期望。
解法
  • k k k 次操作后 a i a_i ai 变成了 a i − b i a_i - b_i aibi,实际每次操作的贡献是操作前后序列乘积的差,即贡献序列是序列乘积的差分,因而总的贡献就是初始序列的乘积减去最终序列的乘积,即求 ∏ i = 1 n a i − ∏ i = 1 n ( a i − b i ) \prod \limits_{i = 1}^{n}a_i - \prod\limits_{i = 1}^{n}(a_i - b_i) i=1naii=1n(aibi) 的期望。
  • 显然只要考虑后半部分,用 E G F \mathbb {EGF} EGF 推导所有方案的贡献之和,再除以总方案数即为期望。

F i ( x ) = ∑ j ≥ 0 a i − j j ! x j = ( a i − x ) e x F ( x ) = ∏ i = 1 n F i ( x ) = e n x ∏ i = 1 n ( a i − x ) F_i(x) = \sum\limits_{j \ge 0}\frac{a_i - j}{j!}x^j = (a_i - x)e^x \\ F(x) = \prod\limits_{i = 1}^{n}F_i(x) = e^{nx} \prod\limits_{i = 1}^{n}(a_i - x) Fi(x)=j0j!aijxj=(aix)exF(x)=i=1nFi(x)=enxi=1n(aix)

  • F ( x ) F(x) F(x) 后半部分可以暴力卷积,同时我们只关心 [ x k ] F ( x ) [x^k]F(x) [xk]F(x), 将前半部分展开后直接计算即可。
典例2 有标号(弱连通)DAG计数
题目大意
  • n n n 个点的有标号(弱连通)DAG 计数。
  • n ≤ 1 0 5 n \le 10^5 n105,答案对 998244353 取模。
解法
  • f n f_n fn 表示 n n n 个点有标号 DAG 的个数,考虑枚举入度为 0 的点的个数 j j j 进行容斥,则这 j j j 个点可与剩下 i − j i - j ij 个点任意连边,有:
    f i = ∑ j = 1 i ( − 1 ) j + 1 ( i j ) 2 j ( i − j ) f i − j f_i = \sum \limits_{j = 1}^{i}(-1)^{j+1}\binom{i}{j}2^{j(i - j)}f_{i - j} fi=j=1i(1)j+1(ji)2j(ij)fij
  • j ( i − j ) j(i - j) j(ij) 组合意义可知 (常用的变换技巧)
    j ( i − j ) = ( i 2 ) − ( j 2 ) − ( i − j 2 ) j(i - j) = \binom{i}{2} - \binom{j}{2} - \binom{i - j}{2} j(ij)=(2i)(2j)(2ij)
  • 代入后得到:
    f i = ∑ j = 1 i ( − 1 ) j + 1 i ! j ! ( i − j ) ! 2 ( i 2 ) 2 ( j 2 ) 2 ( i − j 2 ) f i − j f i i ! 2 ( i 2 ) = ∑ j = 1 i ( − 1 ) j + 1 j ! 2 ( j 2 ) f i − j ( i − j ) ! 2 ( i − j 2 ) \begin{aligned} f_i &= \sum \limits_{j = 1}^{i}(-1)^{j+1}\frac{i!}{j!(i -j)!}\frac{2^{\binom{i}{2}}}{2^{\binom{j}{2}}2^{\binom{i - j}{2}}}f_{i - j}\\ \frac{f_i}{i!2^{\binom{i}{2}}} &= \sum \limits_{j = 1}^{i}\frac{(-1)^{j + 1}}{j!2^{\binom{j}{2}}}\frac{f_{i - j}}{(i - j)!2^{\binom{i - j}{2}}} \\ \end{aligned} fii!2(2i)fi=j=1i(1)j+1j!(ij)!i!2(2j)2(2ij)2(2i)fij=j=1ij!2(2j)(1)j+1(ij)!2(2ij)fij
  • F ( x ) = ∑ i ≥ 0 f i i ! 2 ( i 2 ) x i F(x) = \sum \limits_{i \ge 0}\frac{f_i}{i!2^{\binom{i}{2}}}x^i F(x)=i0i!2(2i)fixi G ( x ) = ∑ i ≥ 1 ( − 1 ) i + 1 i ! 2 ( i 2 ) x i G(x) = \sum \limits_{i\ge 1}\frac{(-1)^{i + 1}}{i!2^{\binom{i}{2}}}x^i G(x)=i1i!2(2i)(1)i+1xi,上式写为:
    F = F G + 1 ⇔ F = 1 1 − G F = FG + 1 \Leftrightarrow F = \frac{1}{1 - G} F=FG+1F=1G1
  • 再设 h n h_n hn 表示 n n n 个点有标号弱连通 DAG 的个数, P ( x ) = ∑ i ≥ 0 f i i ! x i , Q ( x ) = ∑ i ≥ 0 h i i ! x i P(x) = \sum \limits_{i \ge 0}\frac{f_i}{i!}x^i, Q(x) = \sum \limits_{i \ge 0}\frac{h_i}{i!}x^i P(x)=i0i!fixi,Q(x)=i0i!hixi,由多项式 exp ⁡ \exp exp 的意义可知:
    Q ( x ) = ln ⁡ P ( x ) Q(x) = \ln P(x) Q(x)=lnP(x)
  • 总时间复杂度 O ( n log ⁡ n ) \mathcal O(n \log n) O(nlogn)

常见技巧

  • 将生成函数 A ( x ) A(x) A(x) 乘上 1 1 − x \frac{1}{1-x} 1x1 相当于乘上 ∑ i ≥ 0 x i \sum \limits_{i\ge0}x^i i0xi, 实际上就是对 A ( x ) A(x) A(x) 的系数求前缀和,乘 ( 1 1 − x ) k (\frac{1}{1-x})^k (1x1)k 即求 k k k 次前缀和,参见组合数学高阶前缀和部分。

  • 欲求 ∏ j = 1 m ( 1 − x c j ) \prod\limits_{j = 1}^{m}(1 - x^{c_j}) j=1m(1xcj),其中 ∑ j = 1 m c i = n \sum \limits_{j = 1}^{m}c_i = n j=1mci=n,通常有两种处理方法:

    • 分治 NTT,时间复杂度 O ( n log ⁡ n log ⁡ m ) \mathcal O(n \log n \log m) O(nlognlogm)

    • 根据 Taylor 级数,预处理 cnt i \text{cnt}_i cnti 表示数组 c c c i i i 的出现次数,
      ∏ j = 1 m ( 1 − x c j ) = exp ⁡ ∑ j = 1 m ln ⁡ ( 1 − x c j ) = exp ⁡ ( − ∑ j = 1 m ∑ i ≥ 1 x c j i i ) = exp ⁡ ( − ∑ i = 1 n ∑ i ∣ j i cnt i x j j ) \begin{aligned} \prod\limits_{j = 1}^{m}(1 - x^{c_j}) &= \exp \sum \limits_{j = 1}^{m}\ln(1 - x ^{c_j}) \\ &= \exp \left(-\sum \limits_{j = 1}^{m} \sum \limits_{i \ge 1} \dfrac{x^{c_ji}}{i}\right)\\ &= \exp \left(-\sum \limits_{i = 1}^{n}\sum \limits_{i|j}\frac{i\text{cnt}_ix^{j}}{j}\right)\\ \end{aligned} j=1m(1xcj)=expj=1mln(1xcj)=exp(j=1mi1ixcji)=exp i=1nijjicntixj
      时间复杂度 O ( n log ⁡ n ) \mathcal O(n \log n) O(nlogn)

集合幂级数

  • 类比数列的生成函数,设全集 U = { 1 , 2 , … , n } U = \{1,2,\dots,n\} U={1,2,,n},定义集合幂级数:
    f = ∑ S ∈ 2 U f S x S f = \sum\limits_{S\in 2^U}f_S x^S f=S2UfSxS
  • 加减法定义为系数相加减,乘法定义为:
    h = f ∗ g ⇔ ∑ S ∈ 2 U h S x S = ( ∑ S ∈ 2 U f S x S ) ( ∑ S ∈ 2 U g S x S ) = ∑ S ∈ 2 U ( ∑ L ∈ 2 U ∑ R ∈ 2 U [ L ∗ R = S ] f L g R ) x S h = f * g \Leftrightarrow \sum \limits_{S \in 2^U}h_Sx^S = \left(\sum\limits_{S \in 2^U}f_Sx^S\right)\left(\sum\limits_{S \in 2^U}g_Sx^S\right) = \sum\limits_{S\in 2^U}\left(\sum \limits_{L\in 2^U}\sum\limits_{R\in 2^U}[L*R=S] f_Lg_R\right)x^S h=fgS2UhSxS=(S2UfSxS)(S2UgSxS)=S2U(L2UR2U[LR=S]fLgR)xS
  • * 取不同的运算时会产生不同的效果。

OR 卷积

  • * 为或运算,则有 h S = ∑ L ∈ 2 U ∑ R ∈ 2 U [ L ∪ R = S ] f L g R h_S = \sum \limits_{L\in 2^U}\sum\limits_{R\in 2^U}[L\cup R=S]f_Lg_R hS=L2UR2U[LR=S]fLgR

  • 定义 f f f莫比乌斯变换 为集合幂级数 f ^ \hat{f} f^,其中 f S ^ = ∑ T ⊆ S f T \hat{f_S} = \sum\limits_{T \subseteq S}f_T fS^=TSfT,则 h S ^ = f S ^ g S ^ \hat{h_S} = \hat{f_S}\hat{g_S} hS^=fS^gS^

  • 由容斥原理,定义 f ^ \hat{f} f^莫比乌斯反演 f f f,其中 f S = ∑ T ⊆ S ( − 1 ) ∣ S ∣ − ∣ T ∣ f T ^ f_S = \sum\limits_{T \subseteq S}(-1)^{|S| - |T|}\hat{f_T} fS=TS(1)STfT^

  • 可将莫比乌斯变换视为高维前缀和,将莫比乌斯反演视为其逆过程,卷积只需要在对应位置相乘即可,时间复杂度 O ( n 2 n ) \mathcal O(n2^n) O(n2n)

AND 卷积

  • * 为与运算,同 OR 卷积的区别仅在于 0/1 位置互换,其余过程完全一致。

XOR 卷积

  • * 为异或运算,则有 h S = ∑ L ∈ 2 U ∑ R ∈ 2 U [ L ⊕ R = S ] f L g R h_S = \sum \limits_{L\in 2^U}\sum\limits_{R\in 2^U}[L \oplus R=S]f_Lg_R hS=L2UR2U[LR=S]fLgR
  • 注意到 1 2 n ∑ T ∈ 2 U ( − 1 ) ∣ T ∩ S ∣ = [ S = ∅ ] \dfrac{1}{2^n} \sum \limits_{T\in 2^U}(-1)^{|T\cap S|}=[S = \varnothing] 2n1T2U(1)TS=[S=],证明即考虑 S S S 不为空时,任取 v ∈ S v \in S vS T T T T ⊕ v T \oplus v Tv 产生贡献的和恒为 0。
  • 因而可化简得到:
    h S = ∑ L ∈ 2 U ∑ R ∈ 2 U [ L ⊕ R ⊕ S = ∅ ] f L g R = ∑ L ∈ 2 U ∑ R ∈ 2 U 1 2 n ∑ T ∈ 2 U ( − 1 ) ∣ T ∩ ( L ⊕ R ⊕ S ) ∣ f L g R = ∑ L ∈ 2 U ∑ R ∈ 2 U 1 2 n ∑ T ∈ 2 U ( − 1 ) ∣ T ∩ L ∣ ( − 1 ) ∣ T ∩ R ∣ ( − 1 ) ∣ T ∩ S ∣ f L g R = 1 2 n ∑ T ∈ 2 U ( − 1 ) ∣ T ∩ S ∣ ( ∑ L ∈ 2 U ( − 1 ) ∣ T ∩ L ∣ f L ) ( ∑ R ∈ 2 U ( − 1 ) ∣ T ∩ R ∣ g R ) \begin{aligned} h_S &= \sum \limits_{L \in 2^U}\sum\limits_{R\in 2^U}[L\oplus R\oplus S=\varnothing]f_Lg_R \\ &= \sum \limits_{L \in 2^U}\sum\limits_{R\in 2^U}\dfrac{1}{2^n}\sum \limits_{T\in 2^U}(-1)^{|T\cap(L\oplus R\oplus S)|}f_Lg_R \\ &= \sum \limits_{L \in 2^U}\sum\limits_{R\in 2^U}\dfrac{1}{2^n}\sum \limits_{T\in 2^U}(-1)^{|T\cap L|}(-1)^{|T\cap R|}(-1)^{|T\cap S|}f_Lg_R \\ &= \dfrac{1}{2^n}\sum\limits_{T\in 2^U}(-1)^{|T\cap S|}\left(\sum\limits_{L\in 2^U}(-1)^{|T\cap L|}f_L\right)\left(\sum\limits_{R\in 2^U}(-1)^{|T\cap R|}g_R\right)\\ \end{aligned} hS=L2UR2U[LRS=]fLgR=L2UR2U2n1T2U(1)T(LRS)fLgR=L2UR2U2n1T2U(1)TL(1)TR(1)TSfLgR=2n1T2U(1)TS(L2U(1)TLfL)(R2U(1)TRgR)
  • 根据上述结果(可取 g = x ∅ g = x^{\varnothing} g=x)定义 f f f沃尔什变换 为集合幂级数 f ^ \hat{f} f^ f ^ \hat{f} f^沃尔什逆变换 f f f,其中:
    f S ^ = ∑ T ∈ 2 U ( − 1 ) ∣ S ∩ T ∣ f T ⇔ f S = 1 2 n ∑ T ∈ 2 U ( − 1 ) ∣ S ∩ T ∣ f T ^ \hat{f_S} = \sum\limits_{T\in 2^U}(-1)^{|S\cap T|}f_T \Leftrightarrow f_S = \dfrac{1}{2^n}\sum\limits_{T\in 2^U}(-1)^{|S\cap T|}\hat{f_T} fS^=T2U(1)STfTfS=2n1T2U(1)STfT^
  • 此时便满足 h s ^ = f s ^ g s ^ \hat{h_s} = \hat{f_s} \hat{g_s} hs^=fs^gs^
  • 考虑怎样快速求一个集合幂级数 f f f 的沃尔什变换,沃尔什逆变换只需乘上 1 2 n \dfrac{1}{2^n} 2n1 的系数即可。
  • f S ^ ( i ) = ∑ S ⊕ T ⊆ { 1 , 2 , … , i } ( − 1 ) ∣ S ∩ T ∩ { 1 , 2 , … , i } ∣ \hat{f_S}^{(i)} = \sum \limits_{S\oplus T\subseteq\{1,2,\dots,i\}}(-1)^{|S\cap T \cap\{1,2,\dots,i\}|} fS^(i)=ST{1,2,,i}(1)ST{1,2,,i},则有 f S ^ ( 0 ) = f S , f S ^ ( n ) = f S ^ \hat{f_S}^{(0)} = f_S,\hat{f_S}^{(n)} = \hat{f_S} fS^(0)=fS,fS^(n)=fS^,不难得到:
    f S ^ ( i ) = f S ^ ( i − 1 ) + f ^ S ∪ { i } ( i − 1 )    f ^ S ∪ { i } ( i ) = f S ^ ( i − 1 ) − f ^ S ∪ { i } ( i − 1 ) \hat{f_S}^{(i)} = \hat{f_S}^{(i-1)}+\hat{f}_{S\cup\{i\}}^{(i - 1)}\ \ \hat{f}_{S\cup\{i\}}^{(i)} = \hat{f_S}^{(i-1)}-\hat{f}_{S\cup\{i\}}^{(i - 1)} fS^(i)=fS^(i1)+f^S{i}(i1)  f^S{i}(i)=fS^(i1)f^S{i}(i1)
  • 顺序枚举 i i i 从 1 到 n n n 迭代即可,时间复杂度 O ( n 2 n ) \mathcal O(n2^n) O(n2n)

子集卷积

  • 定义 h = f ⋅ g h = f · g h=fg 表示 f f f g g g 的子集卷积,其中 h h h 为集合幂级数:
    h S = ∑ L ∈ 2 U ∑ R ∈ 2 U [ L ∪ R = S ] [ L ∩ R = ∅ ] f L g R h_S = \sum \limits_{L\in 2^U}\sum\limits_{R\in 2^U}[L \cup R = S][L \cap R = \varnothing] f_Lg_R hS=L2UR2U[LR=S][LR=]fLgR
  • 注意到 [ L ∪ R = S ] [ L ∩ R = ∅ ] = [ L ∪ R = S ] [ ∣ L ∣ + ∣ R ∣ = ∣ S ∣ ] [L \cup R = S][L \cap R = \varnothing] = [L\cup R = S][|L| + |R| = |S|] [LR=S][LR=]=[LR=S][L+R=S],可设集合占位幂级数(其中 z a ∗ z b = z a + b z^a * z^b = z^{a + b} zazb=za+b):
    σ = ∑ S ∈ 2 U f S z ∣ S ∣ x S \sigma = \sum \limits_{S\in 2^U}f_Sz^{|S|}x^{S} σ=S2UfSzSxS
  • 对集合幂级数做 OR 卷积时用快速莫比乌斯变换优化,对 z z z 做卷积时暴力即可,时间复杂度 O ( n 2 2 n ) \mathcal O(n^22^n) O(n22n)
  • 也可将上述四种卷积变换的结果理解成点值,做多项式运算时只需在最外层变换和反演(逆变换)一次。
const int mod = 1e9 + 9;
const int inv2 = mod + 1 >> 1;

inline void orFMT(int *f, int n, int opt)
{
	int s = 1 << n;
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < s; ++j)
			if (!(j >> i & 1))
				opt == 1 ? add(f[1 << i | j], f[j]) : dec(f[1 << i | j], f[j]);
}

inline void andFMT(int *f, int n, int opt)
{
	int s = 1 << n;
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < s; ++j)
			if (!(j >> i & 1))
				opt == 1 ? add(f[j], f[1 << i | j]) : dec(f[j], f[1 << i | j]);
}

inline void FWT(int *f, int n, int opt)
{
	int s = 1 << n;
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < s; ++j)
			if (!(j >> i & 1))
			{
				int tj = 1 << i | j;
				int l = f[j],
					r = f[tj];
				f[j] = f[tj] = l;
				add(f[j], r);
				dec(f[tj], r);
			}	
	if (opt == -1)
	{
		int inv_s = 1;
		for (int i = 0; i < n; ++i)
			inv_s = 1ll * inv2 * inv_s % mod;
		for (int i = 0; i < s; ++i)
			f[i] = 1ll * f[i] * inv_s % mod;
	}
}

// 以下函数传入后 f,g 内的信息都将改变,结果存入 f

inline void polyBit(void (*trans)(int*, int, int), int *f, int *g, int n)
{   // And Or Xor 卷积 
	trans(f, n, 1);
	trans(g, n, 1);
	int s = 1 << n;
	for (int i = 0; i < s; ++i)
		f[i] = 1ll * f[i] * g[i] % mod;
	trans(f, n, -1);
}

inline void polySubset(int *f, int *g, int n)
{
	static int tf[N][S], tg[N][S], th[N][S], cnt[S];
	int s = 1 << n;
	for (int i = 1; i < s; ++i)
		cnt[i] = cnt[i ^ (i & -i)] + 1;
	for (int i = 0; i < s; ++i)
		tf[cnt[i]][i] = f[i], tg[cnt[i]][i] = g[i];
	for (int i = 0; i <= n; ++i)
		orFMT(tf[i], n, 1), orFMT(tg[i], n, 1);
	for (int i = 0; i <= n; ++i)
		for (int j = 0; j <= i; ++j)
			for (int k = 0; k < s; ++k)
				th[i][k] = (1ll * tf[j][k] * tg[i - j][k] + th[i][k]) % mod;
	for (int i = 0; i <= n; ++i)
		orFMT(th[i], n, -1);
	for (int i = 0; i < s; ++i)
		f[i] = th[cnt[i]][i];
	
	for (int i = 0; i < s; ++i)
		cnt[i] = 0;
	for (int i = 0; i <= n; ++i)
		for (int j = 0; j < s; ++j)
			tf[i][j] = tg[i][j] = th[i][j] = 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值