文章目录
O ( l o g N ) O(logN) O(logN) 计算两个多项式的卷积 .
单 位 根 为 在 单 位 圆 上 的 点 . 横 坐 标 为 实 部 , 纵 坐 标 为 虚 部 的 复 数 . 单位根为在单位圆上的点.\\ 横坐标为实部, 纵坐标为虚部的复数 . 单位根为在单位圆上的点.横坐标为实部,纵坐标为虚部的复数.
如图, 图中落在
x
x
x 正半轴的那个红点称为
w
n
0
w_{n}^0
wn0, 被称为
n
n
n 次单位根, 其余的点从
w
n
0
w_n^0
wn0 逆时针顺序分别称为
w
n
1
,
w
n
2
.
.
.
w
n
7
w_n^1, w_n^2...w_n^7
wn1,wn2...wn7 .
从图中可以 感性 得到 单位根 的 2 2 2 个性质 ↓ \downarrow ↓
-
性 质 1 : 性质1: 性质1: w 2 n 2 k = w n k w_{2n}^{2k}=w_n^k w2n2k=wnk
-
性 质 2 : 性质2: 性质2: w n k + n 2 = − w n k w_n^{k+\frac{n}{2}} = -w_n^k wnk+2n=−wnk
下面给出 理性 证明, 首先要知道
欧 拉 公 式 : w n k = c o s ( k ∗ 2 π n ) + i ∗ s i n ( k ∗ 2 π n ) 欧拉公式:w_n^k=cos(k*\frac{2 \pi}{n}) + i*sin(k*\frac{2 \pi}{n}) 欧拉公式:wnk=cos(k∗n2π)+i∗sin(k∗n2π)
其中 i i i 为 − 1 \sqrt{-1} −1 .
-
性
质
1
:
性质1:
性质1:
w
2
n
2
k
=
w
n
k
w_{2n}^{2k}=w_n^k
w2n2k=wnk
证 : 证: 证:
w 2 n 2 k = c o s ( 2 k ∗ 2 π 2 n ) + i ∗ s i n ( 2 k ∗ 2 π 2 n ) = w n k w_{2n}^{2k} = cos(2k*\frac{2 \pi}{2n}) + i*sin(2k*\frac{2 \pi}{2n})=w_n^k w2n2k=cos(2k∗2n2π)+i∗sin(2k∗2n2π)=wnk
-
性
质
2
:
性质2:
性质2:
w
n
k
+
n
2
=
−
w
n
k
w_n^{k+\frac{n}{2}} = -w_n^k
wnk+2n=−wnk
证 : 证: 证:
w n k + n 2 = c o s [ ( k + n 2 ) ∗ 2 π n ] + i ∗ s i n [ ( k + n 2 ) ∗ 2 π n ) ] = − w n k w_n^{k+\frac{n}{2}}\\ =cos[(k+\frac{n}{2})*\frac{2\pi}{n}]+i*sin[(k+\frac{n}{2})*\frac{2\pi}{n})]\\ =-w_n^k wnk+2n=cos[(k+2n)∗n2π]+i∗sin[(k+2n)∗n2π)]=−wnk
-
F F T 的 内 容 FFT的内容 FFT的内容
-
有多项式 A ( x ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a n − 1 x n − 1 A(x)=a_0 + a_1x+a_2x^2+\dotsi+a_{n-1}x^{n-1} A(x)=a0+a1x+a2x2+⋯+an−1xn−1
∵
一
个
n
次
多
项
式
可
以
被
n
+
1
个
点
唯
一
确
定
.
\because 一个n次多项式可以被n+1个点唯一确定.
∵一个n次多项式可以被n+1个点唯一确定.
∴
将
w
n
0
⋯
w
n
n
−
1
代
入
可
以
确
定
该
多
项
式
.
\therefore 将 w_n^0 \dotsi w_n^{n-1} 代入可以确定该多项式.
∴将wn0⋯wnn−1代入可以确定该多项式.
这里默认 n n n 为 2 2 2 的整数次幂, 所以 n − 1 n-1 n−1 为奇数 .
- 对于上方多项式
A
(
x
)
A(x)
A(x), 将其 系数 按奇偶分开, 得
↓
↓
↓
A ( x ) = ( a 0 + a 2 ∗ x 2 + ⋯ + a n − 2 ∗ x n − 2 ) + ( a 1 ∗ x + a 3 ∗ x 3 + ⋯ + a n − 1 ∗ x n − 1 ) A(x) = (a_0+a_2*x^2+ \dotsi + a_{n-2}*x^{n-2})+(a_1*x+a_3*x^3+ \dotsi + a_{n-1}*x^{n-1}) A(x)=(a0+a2∗x2+⋯+an−2∗xn−2)+(a1∗x+a3∗x3+⋯+an−1∗xn−1)
设 设 设
A 1 ( x ) = a 0 + a 2 x + a 4 x 2 + ⋯ + a n − 2 x n 2 − 1 A 2 ( x ) = a 1 + a 3 x + a 5 x 2 + ⋯ + a n − 1 x n 2 − 1 A_1(x)=a_0+a_2x+a_4x^2+ \dotsi+a_{n-2}x^{\frac{n}{2}-1}\\ \ \\ A_2(x)=a_1+a_3x+a_5x^2+ \dotsi+a_{n-1}x^{\frac{n}{2}-1} A1(x)=a0+a2x+a4x2+⋯+an−2x2n−1 A2(x)=a1+a3x+a5x2+⋯+an−1x2n−1
则 则 则
A ( x ) = A 1 ( x 2 ) + x ∗ A 2 ( x 2 ) A(x)=A_1(x^2)+x*A_2(x^2) A(x)=A1(x2)+x∗A2(x2)
代
入
w
n
k
(
k
<
n
2
)
得
代入\ w_n^k\ \ \ \ (k < \frac{n}{2})\ 得
代入 wnk (k<2n) 得
A
(
w
n
k
)
=
A
1
(
w
n
2
k
)
+
w
n
k
A
2
(
w
n
2
k
)
=
A
1
(
w
n
2
k
)
+
w
n
k
A
2
(
w
n
2
k
)
A(w_n^k) = A_1(w_n^{2k})+w_n^kA_2(w_n^{2k})=A_1(w_{\frac{n}{2}}^k)+w_n^kA_2(w_{\frac{n}{2}}^k)
A(wnk)=A1(wn2k)+wnkA2(wn2k)=A1(w2nk)+wnkA2(w2nk)
代
入
w
n
k
+
n
2
得
代入\ w_n^{k+\frac{n}{2}}\ 得
代入 wnk+2n 得
A
(
w
n
k
+
n
2
)
=
A
1
(
w
n
2
k
+
n
)
+
w
n
k
+
n
2
A
2
(
w
n
2
k
+
n
)
=
A
1
(
w
n
2
k
∗
w
n
n
)
−
w
n
k
A
2
(
w
n
2
k
w
n
n
)
=
A
1
(
w
n
2
k
)
−
w
n
k
A
2
(
w
n
2
k
)
=
A
1
(
w
n
2
k
)
−
w
n
k
A
2
(
w
n
2
k
)
A(w_n^{k+\frac{n}{2}})\\\ \\=A_1(w_n^{2k+n})+w_n^{k+\frac{n}{2}}A_2(w_n^{2k+n})\\ \ \\ =A_1(w_n^{2k}*w_n^n)-w_n^kA_2(w_n^{2k}w_n^n)\\ \ \\ =A_1(w_n^{2k})-w_n^kA_2(w_n^{2k}) \\ \ \\ =A_1(w_{\frac{n}{2}}^k)-w_n^kA_2(w_{\frac{n}{2}}^k)
A(wnk+2n) =A1(wn2k+n)+wnk+2nA2(wn2k+n) =A1(wn2k∗wnn)−wnkA2(wn2kwnn) =A1(wn2k)−wnkA2(wn2k) =A1(w2nk)−wnkA2(w2nk)
结 论 : 结论: 结论:
-
A ( w n k ) = A 1 ( w n 2 k ) + w n k A 2 ( w n 2 k ) A(w_n^k) = A_1(w_{\frac{n}{2}}^k)+w_n^kA_2(w_{\frac{n}{2}}^k) A(wnk)=A1(w2nk)+wnkA2(w2nk)
-
A ( w n k + n 2 ) = A 1 ( w n 2 k ) − w n k A 2 ( w n 2 k ) A(w_n^{k+\frac{n}{2}})=A_1(w_{\frac{n}{2}}^k)-w_n^kA_2(w_{\frac{n}{2}}^k) A(wnk+2n)=A1(w2nk)−wnkA2(w2nk)
可以观察到第二个式子与第一个式子的唯一区别就是正负号 .
根据上方式子分治处理出每个子项, 再向上合并, 时间复杂度 O ( N l o g N ) O(NlogN) O(NlogN) .
总 结 : 总结: 总结:
F F T FFT FFT是先将多项式转换为 点值表示, 然后进行分治处理, 再转变为多项式的 O ( n l o g n ) O(nlogn) O(nlogn) 算法 .
推导过程:
- 把 系 数 按 奇 偶 分 开 把系数按奇偶分开 把系数按奇偶分开
- 代入 单位根 .
- 得到 “分治向上递推式” .
借用 这位大佬 的一个图,
将原序列进行二进制翻转的变换 .
r e v [ i ] = ( r e v [ i > > 1 ] > > 1 ) ∣ ( ( i & 1 ) < < b i t _ n u m − 1 ) rev[i] = (rev[i>>1]>>1) | ((i\&1) << bit\_num-1) rev[i]=(rev[i>>1]>>1)∣((i&1)<<bit_num−1)
详解点击 这里 .
下面是 模板 代码 .
#include<bits/stdc++.h>
#define reg register
const int maxn = 1e6 + 5;
const double Pi = acos(-1);
struct complex{
double x, y;
complex(double x = 0, double y = 0):x(x), y(y) {}
} A[maxn<<2], B[maxn<<2];
complex operator + (complex a, complex b){ return complex(a.x+b.x, a.y+b.y); }
complex operator - (complex a, complex b){ return complex(a.x-b.x, a.y-b.y); }
complex operator * (complex a, complex b){ return complex(a.x*b.x-a.y*b.y, a.x*b.y+a.y*b.x); }
int N, M;
int rev[maxn<<2];
void FFT(complex *F, short Opt){
for(int i = 0; i < N; i ++)
if(i < rev[i]) std::swap(F[i], F[rev[i]]);
for(int p = 2; p <= N; p <<= 1){
int len = p >> 1;
complex tmp(cos(Pi/len), Opt*sin(Pi/len));
for(int i = 0; i < N; i += p){
complex Buf(1, 0);
for(reg int k = i; k < i+len; k ++){
complex Temp = Buf * F[k+len];
F[k+len] = F[k] - Temp;
F[k] = F[k] + Temp;
Buf = Buf * tmp;
}
}
}
}
int main(){
N = read(), M = read();
for(int i = 0; i <= N; i ++) A[i].x = read();
for(int i = 0; i <= M; i ++) B[i].x = read();
int bit_num = 0;
for(M += N, N = 1; N <= M; N <<= 1) bit_num ++;
for(int i = 0; i < N; i ++) rev[i] = (rev[i>>1]>>1)|((i&1)?N>>1:0);
// for(int i = 0; i < N; i ++) rev[i] = (rev[i>>1]>>1) | ((i&1) << bit_num-1);
FFT(A, 1), FFT(B, 1);
for(int i = 0; i < N; i ++) B[i] = A[i] * B[i];
FFT(B, -1);
for(reg int i = 0; i <= M; i ++) printf("%.0lf ", fabs(B[i].x/N));
return 0;
}
- 将单位根 w n w_n wn 替换为原根 g m o d − 1 l e n g^{\frac{mod-1}{len}} glenmod−1
- 最后乘的是 T _ l e n T\_len T_len 的逆元 .
其中 g g g 在 模数 998244353 998244353 998244353下取 3 3 3 .
void FFT(complex *F, int opt){
for(reg int i = 0; i < FFT_Len; i ++)
if(i < rev[i]) std::swap(F[i], F[rev[i]]);
for(reg int p = 2; p <= FFT_Len; p <<= 1){
int half = p >> 1;
complex t = complex(cos(Pi/half), opt*sin(Pi/half));
for(reg int i = 0; i < FFT_Len; i += p){
complex buf = complex(1, 0);
for(reg int k = i; k < i+half; k ++){
complex Tmp = buf * F[k + half];
F[k + half] = F[k] - Tmp;
F[k] = F[k] + Tmp;
buf = buf * t;
}
}
}
}
void NTT(int *f, int opt){
for(reg int i = 0; i < T_len; i ++)
if(i < rev[i]) std::swap(f[i], f[rev[i]]);
for(reg int p = 2; p <= T_len; p <<= 1){
int half = p >> 1;
int wn = Ksm(3, (mod-1)/p);
if(opt == -1) wn = Ksm(wn, mod-2);
for(reg int i = 0; i < T_len ; i += p){
int buf = 1;
for(reg int k = i; k < i+half; k ++){
int Tmp_1 = 1ll*buf*f[k+half] % mod;
f[k+half] = (f[k] - Tmp_1 + mod) % mod;
f[k] = (f[k] + Tmp_1) % mod;
buf = 1ll*buf*wn % mod;
}
}
}
}