第 6 章 · 02 多项式难点精讲 FFT/NTT/FWT ★ 本节定位:对应 OI Wiki (共 16 篇,重点是 、 、 )。难度:高阶(省选/IOI)。前置依赖:第 6 章 · 01 节数论(原根、模运算)、复数基础( )。本节是难点精讲节(★),对 FFT 的蝶形运算、NTT 的原根替换、FWT 的位运算卷积做 Wiki 之外的慢节奏讲解,作 的配套讲义,不重写 Wiki。 ⚠️ 注意:这是数学章节的顶峰。Wiki 的 写得详尽但节奏快,本节的任务是把"为什么这样算"拆开讲。学完本节建立直觉后,务必回 Wiki 看完整证明和代码。 为什么难 多项式这一节集中了三个高门槛: 复数域与单位根:FFT 用复数 ω = e^(2πi/n),需要复数运算和欧拉公式的直觉。
本节定位:对应 OI Wiki
docs/math/poly/(共 16 篇,重点是fft.md、ntt.md、fwt.md)。难度:高阶(省选/IOI)。前置依赖:第 6 章 · 01 节数论(原根、模运算)、复数基础(docs/math/complex.md)。本节是难点精讲节(★),对 FFT 的蝶形运算、NTT 的原根替换、FWT 的位运算卷积做 Wiki 之外的慢节奏讲解,作docs/math/poly/fft.md的配套讲义,不重写 Wiki。
⚠️ 注意:这是数学章节的顶峰。Wiki 的
fft.md写得详尽但节奏快,本节的任务是把"为什么这样算"拆开讲。学完本节建立直觉后,务必回 Wiki 看完整证明和代码。
多项式这一节集中了三个高门槛:
此外还有 FWT(位运算卷积)和一整套多项式高级运算(求逆/开方/ln/exp/牛顿迭代)。本节重点讲 FFT 蝶形,NTT 和 FWT 讲思路差异,高级运算只点名。
问题:两个 n 次多项式相乘,朴素 O(n²)。FFT 把它降到 O(n log n)。
多项式有两种表示:
A(x) = a0 + a1·x + ... + an·x^n,存系数数组。(x, A(x)) 对。关键:点值表示下,乘法是 O(n)——只要 A、B 取相同点,C = A·B 在这些点上的值就是对应相乘。朴素求值/插值都是 O(n²),瓶颈在"求值"和"插值"。
如果能取"特殊点"让求值变快就好了。FFT 取 n 次单位根 ω_n^k = e^(2πik/n)(k = 0..n-1)。
单位根的三个性质(蝶形分治的基石):
ω_n^n = 1。ω_n^(k + n/2) = -ω_n^k。ω_n^(2k) = ω_(n/2)^k(把 n 个单位根的偶数幂恰好是 n/2 次单位根)。利用折半定理,把 A(x) 按系数下标奇偶拆成 A0(x²)(偶次)和 A1(x²)(奇次):
A(ω_n^k) = A0(ω_n^(2k)) + ω_n^k · A1(ω_n^(2k)) = A0(ω_(n/2)^k) + ω_n^k · A1(ω_(n/2)^k)
于是 n 个点的求值 → 两个 n/2 个点的子问题,分治!每层 O(n),共 O(log n) 层,总计 O(n log n)。
分治到单元素再合并,合并时同一层成对处理,像蝴蝶的翅膀,叫蝶形运算。Cooley-Tukey 算法用迭代(而非递归)实现:
⚠️ 注意:位逆序置换是 FFT 代码里最容易写错的地方。n=8 时,下标 1(001)要换到位置 4(100)。建议先写一个独立的
rev[i]预处理函数,单独调试通过再并入主流程。
逆变换 IDFT 只需把单位根换成 ω_n^(-k),最后除以 n。
FFT 用复数有浮点误差,且不能直接对大整数取模。NTT 用原根替换单位根,把整个计算搬到模意义下。
g^((p-1)/n) mod p 满足单位根的三条性质(周期/对称/折半),所以 NTT 的算法结构和 FFT 完全一样,只是把复数 ω 换成 g^((p-1)/n) mod p。💡 学习提示:理解了 FFT 的蝶形,NTT 就是"换个单位根"。重点记 998244353 这个数和它的原根 3,比赛里看到"模 998244353"基本就是要 NTT。
FWT 解决位运算卷积:c_k = Σ_{i op j = k} a_i b_j,op 是按位或/与/异或。
docs/math/poly/ 还有一整套基于 NTT 的高级运算,都依赖牛顿迭代法(docs/math/poly/newton.md):
docs/math/poly/inv.md):已知 A 求 B 使 A·B ≡ 1。docs/math/poly/sqrt.md 等)。docs/math/poly/comp-rev.md)。这些是省选/IOI 的"工具箱",本节只点名,深入请逐篇读 Wiki。
rev 数组并验证。g^((p-1)/n),别在 NTT 代码里写复数。💡 学习提示:FFT 的"理解"和"写对代码"差距很大。建议先用 n=8 手算一遍位逆序和蝶形,再写代码,最后用模板题验证。手算这一步省不掉。
docs/math/poly/ 各页。