下面的所有知识均属多项式范畴。
前言
本文略去多项式的定义,毕竟初中数学都讲过。
本文会出现一些可能没有见过的表达:
- 对于一个关于 x x x 的多项式 f ( x ) f(x) f(x),有时也称其最高次项次数为 f ( x ) f(x) f(x) 的度数。
- 多项式同样定义了四则运算,包括常数与多项式的四则运算和多项式之间的四则运算,同时也衍生出了同余符号 ≡ \equiv ≡、对数函数 ln ( f ( x ) ) \ln(f(x)) ln(f(x)),指数函数 exp ( f ( x ) ) \exp(f(x)) exp(f(x)) 等等。
- 下文一般采用 f ( x ) m o d x n f(x)~\bmod x^n f(x) modxn 来表示多项式 f ( x ) f(x) f(x) 的所有次数小于 n n n 的项之和。
- 下文有时会采用 [ x k ] f ( x ) [x^k]f(x) [xk]f(x) 表示多项式次数为 k k k 的项。
注意:本文的知识环环相扣,请勿乱序阅读。
拉格朗日插值
插值,通俗地讲,就是指通过多项式在某些点的值求出整个多项式的表达式。
而拉格朗日插值就是计算插值的一种方法,它支持通过 n + 1 n + 1 n+1 个点插出一个度数为 n n n 的多项式。
按惯例,献上模板题。
首先对于多项式 f ( x ) f(x) f(x),必然满足 f ( x ) ≡ f ( a ) ( m o d ( x − a ) ) f(x) \equiv f(a)~(\bmod (x - a)) f(x)≡f(a) (mod(x−a))。
证明:
转化为 f ( x ) − f ( a ) ≡ 0 ( m o d ( x − a ) ) f(x) - f(a) \equiv 0~(\bmod~(x - a)) f(x)−f(a)≡0 (mod (x−a))。
设左侧的多项式为 g ( x ) g(x) g(x),显然其常数项为 0 0 0。
又因为其第 i i i 项为 p i ( x i − a i ) p_i(x^i - a^i) pi(xi−ai)( p i p_i pi 为系数),并且 x i − a i x^i - a^i xi−ai 必定可以分解为 ( x − a ) ⋅ q ( x ) (x - a) \cdot q(x) (x−a)⋅q(x) 的形式( q ( x ) q(x) q(x) 为另一个多项式),所以得证。
所以我们可以得到一个关于 f ( x ) f(x) f(x) 线性同余方程组:
{ f ( x ) ≡ y 1 ( m o d ( x − x 1 ) ) f ( x ) ≡ y 2 ( m o d ( x − x 2 ) ) f ( x ) ≡ y 3 ( m o d ( x − x 3 ) ) ⋮ f ( x ) ≡ y n ( m o d ( x − x n ) ) \left\{ \begin{matrix} f(x) \equiv y_1~(\bmod (x - x_1)) \\ f(x) \equiv y_2~(\bmod (x - x_2)) \\ f(x) \equiv y_3~(\bmod (x - x_3)) \\ \vdots \\ f(x) \equiv y_n~(\bmod (x - x_n)) \end{matrix} \right. ⎩⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎧f(x)≡y1 (mod(x−x1))f(x)≡y2 (mod(x−x2))f(x)≡y3 (mod(x−x3))⋮f(x)≡yn (mod(x−xn))
这里可以使用中国剩余定理解方程组(这里就没有必要关注它们是否互质了)。
我们可以设 M = ∏ i = 1 n ( x − x i ) M = \prod_{i = 1}^n (x - x_i) M=∏i=1n(x−xi), M i = M ( x − x i ) = ∏ j ≠ i ( x − x j ) M_i = \frac{M}{(x - x_i)} = \prod_{j \ne i} (x - x_j) Mi=(x−xi)M=∏j=i(x−xj)。
则 M i M_i Mi 在模 x − x i x - x_i x−xi 意义下的逆元为 ∏ j ≠ i 1 x i − x j \prod_{j \ne i}\frac{1}{x_i - x_j} ∏j=ixi−xj1。
(因为 M i M i − 1 = ∏ j ≠ i x − x j x i − x j ≡ 1 ( m o d ( x − x i ) ) M_i M_i^{-1} = \prod_{j \ne i}\frac{x - x_j}{x_i - x_j} \equiv 1~(\bmod (x - x_i)) MiMi−1=∏j=ixi−xjx−xj≡1 (mod(x−xi)),则对于每一个 j ≠ i j \ne i j=i 都存在 $ x - x_j \equiv x_i - x_j~(\bmod (x - x_i))$,移项可得 x − x i ≡ 0 ( m o d ( x − x i ) ) x - x_i \equiv 0~(\bmod (x - x_i)) x−xi≡0 (mod(x−xi)),这显然是成立的。)
所以有 f ( x ) = ∑ i = 1 n y i ∏ j ≠ i x − x j x i − x j ( m o d M ) f(x) = \sum_{i = 1}^n y_i\prod_{j \ne i} \frac{x - x_j}{x_i - x_j}~(\bmod~M) f(x)=∑i=1nyi∏j=ixi−xjx−xj (mod M),它在模意义下是唯一的。
(实际上有另外的证明方式证明了其在通常意义下也是唯一的,这里略去。)
本题中,我们只需要代入 k k k 求解即可,时间复杂度为 O ( n 2 ) \mathcal{O}(n^2) O(n2)。不过更多时候,我们需要求出其所有系数,也可以做到 O ( n 2 ) \mathcal{O}(n^2) O(n2),甚至还存在更低的复杂度,这里就不展开介绍了。
代码就不贴了。
多项式乘法
多项式乘法的定义,这里就不必多言了,初中数学都讲过。
代数形式:
设有多项式 f ( x ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a n x n f(x) = a_0 + a_1 x+ a_2x^2 + \cdots+ a_nx_n f(x)=a0+a1x+a2x2+⋯+anxn, g ( x ) = b 0 + b 1 x + b 2 x 2 + ⋯ + b m x m g(x) = b_0 + b_1 x+ b_2x^2 + \cdots+ b_mx_m g(x)=b0+b1x+b2x2+⋯+bmxm。
则它们的乘法积为 t ( x ) = c 0 + c 1 x + c 2 x 2 + ⋯ + c n + m x n + m t(x) = c_0 + c_1 x + c_2 x^2 + \cdots +c_{n + m}x^{n + m} t(x)=c0+c1x+c2x2+⋯+cn+mxn+m。
其中,对于 t ( x ) t(x) t(x) 的系数 c k c_k ck 有公式: c k = ∑ i = 0 k a i b k − i c_k = \sum_{i = 0}^k a_i b_{k - i} ck=∑i=0kaibk−i。
可以发现这个公式的形式十分类似卷积。但请注意:它们不是同一个东西,不要弄混淆了。
事实上,多项式乘法就是用于求卷积的。如果您对卷积感兴趣,建议百度。
现在,给出两个多项式 f ( x ) f(x) f(x) 和 g ( x ) g(x) g(x) 的系数,求出它们的乘法积 t ( x ) t(x) t(x)。 f ( x ) f(x) f(x) 和 g ( x ) g(x) g(x) 的次数小于 1 0 5 10^5 105。
根据上面的公式,我们可以得到一个 O ( n 2 ) \mathcal{O}(n^2) O(n2) 的优秀算法。
我们考虑换一种思想计算这个乘法积。
众所周知,有这样一条著名的定理:任意 k k k 个点可以唯一确定一个至多为 k − 1 k - 1 k−1 次的多项式。
所以我们得到了一个新的思路:先求出足够多的 f ( x ) f(x) f(x) 和 g ( x ) g(x) g(x) 的点值,将它们用 O ( n ) \mathcal{O}(n) O(n) 的复杂度乘起来(横坐标不变,纵坐标相乘),再将这些点转化为一个多项式(上面提到了这样的过程叫做插值)。
这种方法叫做离散傅里叶变换(即著名的 DFT \operatorname{DFT} DFT)。
问题是,如果钦定一些 x x x 去求对应的 f ( x ) f(x) f(x) 的值,时间复杂度还是 O ( n 2 ) \mathcal{O}(n^2) O(n2) 的。
所以,下面介绍快速傅里叶变换,同时会介绍快速数论变换(即著名的 FFT \operatorname{FFT} FFT 和 NTT \operatorname{NTT} NTT)。
还记得二次函数 y = x 2 y = x^2 y=x2 的性质吗?
它关于 y y y 轴对称,所以我们如果代入 x = k x = k x=k,同样可以得到 x = − k x = -k x=−k 时 y y y 的值。
这启发我们,可以选取一些特殊的 x x x,使得我们可以在求出一些点值的同时,快速地求出另外的一些点值。
这个时候,单位根就派上用场了。
先讲一些使得单位根能够应用于此的性质:
- 记 ω n \omega_n ωn 为 n n n 次单位根,即满足 ω n k = e 2 k π i n \omega_n^k = e^{\frac{2k\pi i}{n}} ωnk=en2kπi,而 e 2 k π i n = cos 2 k π n + i sin 2 k π n e^{\frac{2k\pi i}{n}}=\cos \frac{2k\pi}{n} + i \sin \frac{2k\pi}{n} en2kπi=cosn2kπ+isinn2kπ, i i i 为虚数单位。
(注意 n n n 次单位根的 n n n 一般取 2 2 2 的正整数次幂,而下文皆如此。)
这个计算公式不仅可以让我们计算出单位根的具体数值,并且可以得出以下性质:
- ω n n = cos 2 π + i sin 2 π = 1 = ω n 0 \omega_n^n = \cos 2\pi + i \sin 2\pi = 1 = \omega_n^0 ωnn=cos2π+isin2π=1=ωn0。
这条性质说明 n n n 次单位根存在长度为 n n n 的指数循环节;
- ω n n 2 = cos π + i sin π = − 1 \omega_n^{\frac{n}{2}} = \cos \pi + i \sin \pi= -1 ωn2n=cosπ+isinπ=−1。
这条性质间接地证明了 n n n 次单位根的最小指数循环节长度为 n n n,同时为下文折半引理的证明做了铺垫。
- 单位根消去引理: ω 2 n 2 k = ω n k \omega_{2n}^{2k} = \omega_n^k ω2n2k=ωnk。
根据公式有 ω 2 n 2 k = e 4 k π i 2 n = e 2 k π i n = ω n k \omega_{2n}^{2k}= e^{\frac{4k\pi i}{2n}} = e^{\frac{2k\pi i}{n}} = \omega_{n}^k ω2n2k=e2n4kπi=en2kπi=ωnk,得证。
这条性质可以做为沟通不同次单位根的桥梁,同时也证明了 n n n 次单位根的值域是 2 n 2n 2n 次单位根值域的真子集。
- 单位根折半引理: ω n k + n 2 = − ω n k \omega_n^{k+\frac{n}{2}} = -\omega_{n}^k ωnk+2n=−ωnk。
根据上面的性质有 ω n k + n 2 = ω n k ⋅ ω n n 2 = − ω n k \omega_n^{k + \frac{n}{2}} = \omega_n^k \cdot \omega_n^{\frac{n}{2}} = -\omega_n^k ωnk+2n=ωnk⋅ωn2n=−ωnk,得证。
这条性质可以做为沟通同次不同指数的单位根的桥梁,为 DFT \operatorname{DFT} DFT 的分治优化创造了可能。
而我们该如何运用单位根的这些性质呢?
我们将 f ( x ) = a 0 + a 1 x + a 2 x 2 + a 3 x 3 f(x) = a_0 + a_1 x + a_2 x^2 + a_3x^3 f(x)=a0+a1x+a2x2+a3x3 做一个小小的改动:
f ( x ) = a 0 + a 1 x + a 2 x 2 + a 3 x 3 = a 0 + a 2 x 2 + a 1 x + a 3 x 3 = ( a 0 + a 2 x 2 ) + x ( a 1 + a 3 x 2 ) = f 1 ( x 2 ) + x f 2 ( x 2 ) \begin{aligned} f(x) &= a_0 + a_1 x + a_2 x^2 + a_3x^3 \\ &=a_0 + a_2 x^2 + a_1 x + a_3x^3 \\ &= (a_0 + a_2 x^2) + x(a_1 + a_3 x^2) \\ &= f_1(x^2) + xf_2(x^2) \end{aligned} f(x)=a0+a1x+a2x2+a3x3=a0+a2x2+a1x+a3x3=(a0+a2x2)+x(a1+a3x2)=f1(x2)+xf2(x2)
可以发现,这样的变换,会使得原多项式被拆分为两个规模缩小了一半多项式。
所以,我们又有了一个新的算法模型:
我们考虑将 f ( x ) f(x) f(x) 拆分为两个规模缩小了一半的多项式 f 1 ( x ) , f 2 ( x ) f_1(x), f_2(x) f1(x),f2(x),然后对它们进行分治,接着,我们可以用 f 1 ( x ) , f 2 ( x ) f_1(x),f_2(x) f1(x),f2(x) 在 x = k 2 x = k^2 x=k2 处的点值计算出 f ( x ) f(x) f(x) 在 x = k x = k x=k 处的点值。如果分治时每一层的复杂度都能够做到 O ( n ) \mathcal{O}(n) O(n),那么总的时间复杂度就是 O ( n log 2 n ) \mathcal{O}(n \log_2 n) O(nlog2n)。
接下来,我们考虑利用单位根的性质,将每一层转化点值的操作复杂度变为 O ( n ) \mathcal{O}(n) O(n)。
如果我们将单位根 ω n k \omega_{n}^k