多项式学习笔记
啊?
“做多项式题就像ke药,出多项式题就像fan毒。” —— 可爱小粉兔
“以快速傅里叶变换为基石的多项式算法赋予了算法竞赛选手直接操纵生成函数的能力。”—— 摘自 OI-wiki
FFT 与 NTT
在多项式题目中,使用快速傅里叶变换、快速数论变换来加速运算或卷积是十分常见的。
它们能通过分治来加速卷积是运用到了复数和原根的奇妙特性。
在 \(n\) 次多项式 \(A\) 与 \(B\) 相乘的积时,我们可以代入点插值后直接乘法计算再转换。(当然也可以直接算)
可是计算一个点值的时间是 \(\mathcal O(n)\) 的,\(n+1\) 个点复杂度就是 \(\mathcal O(n^2)\)
转换成多项式的拉格朗日插值也是 \(\mathcal O(n^2)\) 的。$$f(k) = \sum_{i=0}^{n} y_i \prod_{j \neq i} \frac{k-x_j}{x_i-x_j} $$
而离散傅里叶变换可以在 \(\mathcal O(n \log_2 n)\) 时间内解决问题(\(\text{DFT, IDFT}\))。
这就需要一些多项式推导和分治思想。
有 \(\mathcal O(\log_2 n)\) 层递归,每层花 \(\mathcal O(n)\) 时间处理。
复数 与 原根
首先是复数。
可以发现单位复根在坐标系中的半径为 \(1\) 的圆上。记方程 \(x^n=1\) 的第 \(k\) 个根为 \(\omega_n^k\)(从 \(0\) 开始,可以看作模 \(n\) 的同余系来理解)
算法的基本思想是分治。分治求当 \(x=\omega_n^k\) 的时候 \(f(x)\) 的值。
举个例子,对于一共 \(8\) 项的多项式:
\(f(x) = a_0 + a_1x + a_2x^2+a_3x^3+a_4x^4+a_5x^5+a_6x^6+a_7x^7\)
按照次数的奇偶来分成两组,然后右边提出来一个 \(x\):
分别用奇偶次次项数建立新的函数:
那么原来的 \(f(x)\) 用新函数表示为:
\(f(x)=G(x^2) + x \times H(x^2)\)
到目前为止可能还无法瞥见单位复根的作用,其实单位复根在这里是这样的:
利用 \(\omega^i_n = -\omega^{i + n/2}_n\),和 \(G(x^2)\) 和 \(H(x^2)\) 是偶函数(显然):
可以知道在复平面上 \(\omega^i_n\) 和 \(\omega^{i+n/2}_n\) 的 \(G(x^2)\) 与 \(H(x^2)\) 对应的值相同。
得到:
和:
一加一减。
因此我们求出了 \(G(\omega_{n/2}^k)\) 和 \(H(\omega_{n/2}^k)\) 后,就可以同时求出 \(f(\omega_n^k)\) 和 \(f(\omega_n^{k+n/2})\)。于是对 \(G\) 和 \(H\) 分别递归 \(\text{DFT}\) 即可。
只有复数域中才能构造出这样美丽的值代入。
这样就省去了一半的计算量,递归下去算出一个点就只需要 \(\mathcal O(\log_2 n)\) 次操作了。
一般 \(\text{FFT}\) 都是直接分治操作,用蝴蝶变换算出位置减少常数。
原根怎么你了?
\(\text{NTT}\) 解决的是多项式乘法带模数的情况,可以说有些受模数的限制,数也比较大。目前最常见的模数是 \(998244353\)。(原根是 \(3\),好吃)
阶
由欧拉定理可知,对 \(a\in \mathbf{Z},m\in\mathbf{N}^{*}\),若 \((a,m)=1\),则 \(a^{\varphi(m)}\equiv 1\pmod m\)
因此满足同余式 \(a^n \equiv 1 \bmod m\) 的最小正整数 \(n\) 存在,这个 \(n\) 称作 \(a\) 模 \(m\) 的阶,记作 \(\delta_m(a)\)
可以用阶类比平方。
原根
设 \(m \in \mathbf{N}^{*},g\in \mathbf{Z}\)。 若 \((g,m)=1\),且 \(\delta_m(g)=\varphi(m)\),则称 \(g\) 为模 \(m\) 的原根。
即 \(g\) 满足 \(\delta_m(g) = \left| \mathbf{Z}_m^* \right| = \varphi(m)\)
当 \(m\) 是质数时,我们有 \(g^i \bmod m,\,0 \lt i \lt m\) 的结果互不相同。
从这里就可以看出,原根很像单位复根。
例如 \(m=998244353\) 时,就有
数论变换(\(\text{NTT}\))是通过将离散傅立叶变换化为 \(F={\mathbb{Z}/p}\),整数模质数 \(p\)。这是一个有限域,只要 \(p-1\) 可被 \(n\) 整除,就存在本原 \(n\) 次方根,所以对于正整数 \(\xi\) 我们有 \(p=\xi n+1\) 。
具体来说,对于质数 \(p=qn+1\),原根 \(g\) 满足 \(g^{qn} \equiv 1 \bmod p\), 将 \(g_n := g^q\bmod p\) 看做 \(\omega_n\) 的等价,则其满足相似的性质。
比如 \(g_n^n \equiv 1 \bmod p\) 就有对应的 $ g_n^{n/2} \equiv -1 \bmod p$
因为有:\(p=998244353=7 \times 17 \times 2^{23}+1, g=3\)
所以用 \(998244353\) 做模数时可以方便的将 \(n\) 补到 \(2\) 的幂,然后推出 \(q=\frac{p-1}{n}\),以构造出和单位复根性质相同的东西。(\(2^{23}=8388608\) 完全够用)
把 \(\text{FFT}\) 改一改就是 \(\text{NTT}\) 了。
\(\\ \\ \\ \\ \\\)
多项式基础
多项式取模,多项式求逆,多项式除法,多项式开根,多项式快速幂,多项式ln
这些东西都别急。
\(\\ \\ \\ \\ \\\)
分治 FFT/NTT
例题:现有一转移式形如 \(f(i)=\sum^{i-1}_{j=0}f(j)g(i−j)\),其中终止条件 \(f(0)=1\),给定 \(g(i)\) 求 \(f(i)\),对 \(998244353\) 取模。
首先直接算 \(f_n\) 需要 \(n-1\) 次操作,总共时间是 \(\mathcal O(n^2)\) 的,考虑怎么优化卷积。
发现 \(f\) 的第推式是前项对 \(g\) 的卷积,不能一次性求出。
可以使用分治,将 \(f\) 值域中左半区间对右半区间的贡献提前累加到右半区间。
具体的,设左为 \([l,mid]\) 右为 \([mid+1,r]\) 且有一点 \(k\) 满足 \(k \in [mid,r]\),考虑左区间对 \(f(k)\) 的贡献。
则有 \(v_k=\sum^{mid}_{j=l}f(j)g(k−j)\)。
其中 \(f(j)g(i−j)\) 是一个卷积的形式(而且没有那丑陋的第推式了),所以可以直接把 \(f\) 的 \(l\) 到 \(mid\),\(g\) 的 \(0\) 到 \(r−l\) 提出来,对这一部分进行 \(\text{NTT}\)。
所以左边对右边的所有点的贡献均可以在一次多项式乘法中得出。
用CDQ的形式,总复杂度 \(\mathcal O(n \log^2 n)\)。
\(\\ \\ \\ \\ \\\)
生成函数
你先别急。
《具体数学》怎么你了?
在系统学习多项式之前我一直难以理解生成函数,不知道它的构造方法,含义以及用途。
生成函数(generating function),又称母函数,是一种形式幂级数,其每一项的系数可以提供关于这个序列的信息。

浙公网安备 33010602011771号