前置知识
多项式系数表示法
A(x)=a0+a1x+a2x2+⋯+anxn
系数表示法的好处
可以直接利用参数 x 计算多项式值,即直接代入上式 O(n) 地计算
多项式点值表示法
首先要知道一个定理:任意 n+1 个不同点均可以确定一个 n 次多项式
x1→xn+1:⎩⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎧a0a0a0a0+a1x1+a2x12+⋯+anx1n=y1+a1x2+a2x22+⋯+anx2n=y2…+a1xn+a2xn2+⋯+anxnn=yn+a1xn+1+a2xn+12+⋯+anxn+1n=yn+1
⎝⎜⎜⎜⎜⎜⎛11…1x1x2xn+1x12x22xn+12………x1nx2nxn+1n⎠⎟⎟⎟⎟⎟⎞=1≤i,j≤n+1∏(xi−xj)=0
即使用了 n+1 个特殊点,这个方程组用高斯消元便可以解
点表示法的好处
A(x)B(x) 有 [0,n+m] 次,所以要取 n+m+1 个点
A:(x1,A(x1)),(x2,A(x2)),…,(xn+m+1,A(xn+m+1))
B:(x1,B(x1)),(x2,B(x2)),…,(xn+m+1,B(xn+m+1))
若求 C(xi),C(xi)=A(xi)B(xi)
∴C:(x1,A(x1)B(x1)),(x2,A(x2)B(x2)),…,(xn+m+1,A(xn+m+1)B(xn+m+1))
时间复杂度 O(nm)→O(n+m)
卷积
也就是多项式乘法
对于系数分别为 [a] 的多项式 A 与系数分别为 [b] 的多项式 B ,其卷积出来的多项式 C 的第 k 项系数 ck 为
ck=i+j=k∑aibj=i=0∑kaibk−i
算法引导
我们得到两个系数表示法的多项式,想要卷积出来一个系数表示法的多项式,在乘法时可以借助点表示法去乘从而优化时间,但这样时间复杂度就卡在了系数表示法和点表示法互相转换上面,所以有一个前提
系数表示法 ⇌quick 点值表示法
快速求点值
复数与单位根
引入特殊点:复平面上的单位根
复数:
a+bi
运算:
- 相加:(a+bi)+(c+di)=(a+c)+(b+d)i ,平行四边形对角线
- 相乘:(a+bi)(c+di)=(ac−bd)+(ad+bc)i ,向量长度为两向量长的乘积,复角为两向量复角的和
单位根:
将单位圆从 x 轴 n 等分,获得 n 次单位根
eiθ=cosθ+isinθ
ωnk=cos(2πnk)+isin(2πnk)=e2πnki
单位根性质
1.ωnk=ω2n2k
分成 n 份第 k 个和分成 2n 份第 2k 个是相同的
2.ωnk+2n=−ωnk
3.ωnkωnl=ωnk+l
可以代入欧拉公式证明
ωnk=e2πnki
ωnl=e2πnli
ωnkωnl=eπ2in(k+l)=ωnk+l
其实之所以引入单位根也是因为这个特性,相乘等于系数相加,和 xixj=xi+j 的特性一样
DFT
思想
A(x) 系数:(a0,a1,…,an−1)
代入一组单位根: (ωn0,ωn1,…,ωnn−1) 快速求出 (A(ωn0),A(ωn1),…,A(ωnn−1))
在 A(x) 中看
我们可以用分治的方式将其划分为两个相等的部分,对于每一部分求完直接合并
A(x)=(a0+a2x2+⋯+an−2xn−2)+(a1x1+a3x3+⋯+an−1xn−1)=(a0+a2x2+⋯+an−2xn−2)+x(a1+a3x2+⋯+an−1xn−2)
这里即奇偶划分
然后用 x 表 x2 换元
A1(x)=a0+a2x+a4x2+⋯+an−2x2n−1
A2(x)=a1+a3x+a5x2+⋯+an−1x2n−1
则 A(x)=A1(x2)+A2(x2)
方法
分类讨论一下
k∈[0,2n)
A(ωnk)=A1((ωnk)2)+ωnkA2((xnk)2)=A1(ω2nk)+ωnkA2(ω2nk)
k∈[2n,n−1]
A(ωnk+2n)====A1((ωnk+2n)2)+ωnk+2n((ωnk)2)A1(ωn2k+n)−ωnkA2(ωn2k+n)A1(ωn2kωnn)−ωnkA2(ωn2kωnn)A1(ω2nk)−ωnkA2(ω2nk)
这样分类进行递归,时间复杂度为 O(nlogn)
IDFT
(ωnk,A(ωnk))A(x)=c0+c1x+⋯+cn−1xn−1
令 yk=A(ωnk) ,则 ck=i=0∑n−1yi(ωn−k)i
原系数 ak=nck
想快速求出上面的 c[0,n−1] ,可以把式子当做一个关于 y 的多项式
则 B(x)=y0+y1x+⋯+yn−1xn−1
则 ck=B(ωn−k) 而 B(ωn−k) 的一个个求解还需 FFT 从而推出 ck 与 A(x)
那么本质就是一遍 FFT 推出点表示,再来一遍推出系数表示
第一遍求 A(ωn0)→A(xnn−1)
第二遍求 B(ωn0)→B(ωn−(n−1))
ck=i=0∑n−1yk(ωn−k)i=i=0∑n−1(j=0∑n−1aj(ωni)j)(ωn−k)i=i=0∑n−1(j=0∑n−1aj(ωnj−k)i)=j=0∑n−1aj(i=0∑n−1(ωnj−k)i)
令 S(x)=1+x+x2+⋯+xn
则
S(ωnk):k⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧=0=0S(ωnk)=1+ωnk+ωn2k+⋯+ωn(n−1)kωnk(S(ωnk))=ωnk+ωn2k+⋯+ωn0(1−ωnk)S=0∵k=0:ωnk=1∴S=0S(ωnk)=S(1)=n
=j=0∑n−1(S(ωnj−k))
因为只有在 j=k 时候不得 0 会被记入后面是 n
∴=nak
蝴蝶变换
由于 FFT 本来就涉及很多实数运算,常数非常大,如果此时我们再使用递归的话常数会更大,考虑使用递推来实现
写一组系数与递归后的系数进行对比:
a0000a1001a2010a3011a0a2a4a6000a0100a4010a2110a6a4100a5101a6110a7111a1a3a5a7001a1101a5011a3111a7
发现最后一次操作后,下标与原下标互为翻转: 00012→10002
那么我们可以先得出最后的序列,然后向上进行操作模拟递归的回溯操作
用 revi 代表将 i 二进制翻转后的数
bit 为 n+m 最多需要的二进制位数
rev[i] = (rev[i >> 1] >> 1) | (1 << (bit - 1))
程序
已有最高次为 n 的多项式 A 的系数 [a]
已有最高次为 m 的多项式 B 的系数 [b]
求 A∗B=C 的所有系数
const int N = 3000010;
const double PI = acos(-1.0);
int n, m;
struct Complex {
double x, y;
Complex friend operator + (Complex a, Complex b) { return {a.x + b.x, a.y + b.y}; }
Complex friend operator - (Complex a, Complex b) { return {a.x - b.x, a.y - b.y}; }
Complex friend operator * (Complex a, Complex b) { return {a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x}; }
} a[N], b[N];
int rev[N];
int bit, tot;
inline void FFT (Complex a[], int inv) {
for (int i = 0; i < tot; i ++) if (i < rev[i]) swap(a[i], a[rev[i]]);
for (int mid = 1; mid < tot; mid <<= 1) {
Complex w1 = {cos(PI / mid), inv * sin(PI / mid)};
for (int i = 0; i < tot; i += mid * 2) {
Complex wk = {1, 0};
for (int j = 0; j < mid; j ++, wk = wk * w1) {
Complex x = a[i + j], y = wk * a[i + j + mid];
a[i + j] = x + y, a[i + j + mid] = x - y;
}
}
}
}
int main() {
cin >> n >> m;
for (int i = 0; i <= n; i ++) cin >> a[i].x;
for (int i = 0; i <= m; i ++) cin >> b[i].x;
while ((1 << bit) < n + m + 1) bit ++;
tot = 1 << bit;
for (int i = 0; i < tot; i ++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
FFT(a, 1); FFT(b, 1);
for (int i = 0; i < tot; i ++) a[i] = a[i] * b[i];
FFT(a, -1);
for (int i = 0; i <= n + m; i ++) cout << (int)(a[i].x / tot + 0.5) << " ";
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40