FFT
多项式乘法可以用 \(O(n^{2})\) 逐项得到,但不高效。而 FFT 可以 \(O(nlogn)\) 快速计算。
- 第一步是 系数 \(\rightarrow\) 点值,分别求得 \(A,B\) 两个多项式在 \(n\) 个 \(n\) 次单位根的取值,并将两个多项式在同一个根的取值相乘,作为两个多项式相乘后得到的多项式在 \(n\) 个 \(n\) 次单位根的取值;
- 第二步是 点值 \(\rightarrow\) 系数,在已知待求多项式的 \(n\) 个点坐标的情况下,求多项式的 \(n\) 个未知系数,即傅里叶逆变换 (IDFT)。
FFT 的两种实现方式:
- 递归
- 迭代倍增
模板
// 适合在没有取模要求,且答案较大的情况使用
#include <complex>
const double PI = acos(-1.0);
// 迭代 FFT,a 为复数向量,inverse 表示是否逆变换
void FFT(vector<complex<double>>& a, bool inverse) {
int n = a.size();
// 位逆序置换
for (int i = 1, j = 0; i < n; i++) {
int bit = n >> 1;
for (; j & bit; bit >>= 1) j ^= bit;
j ^= bit;
if (i < j) swap(a[i], a[j]);
}
// 迭代合并
for (int len = 2; len <= n; len <<= 1) {
double ang = 2.0 * PI / len * (inverse ? -1.0 : 1.0);
complex<double> wlen(cos(ang), sin(ang));
for (int i = 0; i < n; i += len) {
complex<double> w(1.0, 0.0);
for (int j = 0; j < len / 2; j++) {
complex<double> u = a[i + j];
complex<double> v = a[i + j + len / 2] * w;
a[i + j] = u + v;
a[i + j + len / 2] = u - v;
w *= wlen;
}
}
}
if (inverse) {
for (int i = 0; i < n; i++) a[i] /= n;
}
}
// 多项式乘法(FFT 版本,返回整数系数,四舍五入)
vector<int> Poly_mul(const vector<int>& a, const vector<int>& b) {
if (a.empty()) return b;
if (b.empty()) return a;
// 小规模暴力乘法(结果不会溢出 int)
if ((ll)a.size() * b.size() <= 5000) {
vector<int> c(a.size() + b.size() - 1, 0);
for (size_t i = 0; i < a.size(); i++) {
for (size_t j = 0; j < b.size(); j++) {
c[i + j] += (ll)a[i] * b[j];
}
}
return c;
}
int need = (int)a.size() + (int)b.size() - 1;
int n = 1;
while (n < need) n <<= 1;
vector<complex<double>> fa(n, 0.0), fb(n, 0.0);
for (size_t i = 0; i < a.size(); i++) fa[i] = a[i];
for (size_t i = 0; i < b.size(); i++) fb[i] = b[i];
FFT(fa, false);
FFT(fb, false);
for (int i = 0; i < n; i++) fa[i] *= fb[i];
FFT(fa, true);
vector<int> res(need);
for (int i = 0; i < need; i++) {
// 四舍五入,处理可能的负零
double val = fa[i].real();
res[i] = (int)llround(val);
}
return res;
}
P3803
模板题,\(O(n\log n)\) 求两个 \(n - 1\) 次多项式相乘后,得到的 \(2n-2\) 次多项式的各项系数。
P1919
FFT 实现高精度乘法
ABC392 G
要找给定数组中满足\(A + C = 2B\)的升序三元组\((A,B,C)\)的数量,可以通过构造多项式的方式巧妙计算:
设\(a=[1,2,3,5]\),要计算这个数组中的三元组数量:
转化为计算数组中每个元素作为\(B\)时的数量,再累加到一起。
例如计算\(B=5\)时的三元组数量,即计算\(A+C=10\)的\((A,C)\)个数。这相当于计算:
\[num(1)*num(9)+num(2)*num(8)+num(3)*num(7)+num(4)*num(6)
\]
其中\(num(i)\)表示数字\(i\)在\(a\)中出现个数。
这个操作就是卷积的计算,而计算卷积的高效算法就是FFT。对于本题,可以构造这样的多项式:
\[(x^{1} + x^{2} + x^{3} + x^{5})
\]
数组中每一项对应指数,而数组中出现了的数系数置为1,没出现的数系数置为0。计算这个多项式的平方,即:
\[(x^{1} + x^{2} + x^{3} + x^{5})(x^{1} + x^{2} + x^{3} + x^{5})
\]
上面多项式的展开结果中的每一项\(b_{i}x^{i}\)即表示\(2B=i\)的\((A,C)\)个数是\(b_{i}\)。具体见代码(用python中的numpy库实现了FFT)


浙公网安备 33010602011771号