已知最高次数不超过
n
−
1
n - 1
n−1 的多项式在平面上的
n
n
n 个点
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
…
,
(
x
n
,
y
n
)
(x_1,y_1),(x_2,y_2),\dots,(x_n,y_n)
(x1,y1),(x2,y2),…,(xn,yn),满足
∀
1
≤
i
<
j
≤
n
,
x
i
≠
x
j
\forall 1 \le i < j \le n, x_i\not=x_j
∀1≤i<j≤n,xi=xj,则可还原出多项式:
f
(
x
)
=
∑
i
=
1
n
y
i
∏
j
≠
i
x
−
x
j
x
i
−
x
j
f(x) = \sum\limits_{i = 1}^{n}y_i\prod\limits_{j \not=i} \frac{x - x_j}{x_i - x_j}
f(x)=i=1∑nyij=i∏xi−xjx−xj
若
x
i
=
i
x_i = i
xi=i,且
x
>
n
x > n
x>n,则该式可简化为:
f
(
x
)
=
∏
i
=
1
n
(
x
−
i
)
(
∑
i
=
1
n
(
−
1
)
n
−
i
y
i
(
x
−
i
)
(
i
−
1
)
!
(
n
−
i
)
!
)
f(x) = \prod\limits_{i = 1}^{n}(x - i)\left(\sum\limits_{i = 1}^{n}\frac{(-1)^{n-i}y_i}{(x-i)(i-1)!(n - i)!} \right)
f(x)=i=1∏n(x−i)(i=1∑n(x−i)(i−1)!(n−i)!(−1)n−iyi)
预处理相关逆元和前缀积,即可
O
(
n
)
\mathcal O(n)
O(n) 计算。
若已知二元多项式在空间内的
n
m
nm
nm 个点
(
x
11
,
y
11
,
z
11
)
,
…
,
(
x
n
m
,
y
n
m
,
z
n
m
)
(x_{11},y_{11},z_{11}),\dots,(x_{nm},y_{nm},z_{nm})
(x11,y11,z11),…,(xnm,ynm,znm),满足任意两点横纵坐标至少一个不相等,且该多项式中
x
x
x 的最高次数不超过
n
−
1
n - 1
n−1,
y
y
y 的最高次数不超过
m
−
1
m - 1
m−1,则可还原出多项式:
f
(
x
,
y
)
=
∑
i
=
1
n
∑
j
=
1
m
z
i
j
∏
k
≠
i
x
−
x
k
j
x
i
j
−
x
k
j
∏
l
≠
j
x
−
x
i
l
x
i
j
−
x
i
l
f(x,y) = \sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{m}z_{ij}\prod\limits_{k\not=i}\frac{x-x_{kj}}{x_{ij}-x_{kj}}\prod\limits_{l\not = j}\frac{x-x_{il}}{x_{ij}-x_{il}}
f(x,y)=i=1∑nj=1∑mzijk=i∏xij−xkjx−xkjl=j∏xij−xilx−xil
FFT
即快速傅里叶变换。
设
n
n
n 次单位根
ω
n
k
=
cos
2
π
k
n
+
i
sin
2
π
k
n
\omega_{n}^k = \cos\frac{2\pi k}{n} + i\sin\frac{2\pi k}{n}
ωnk=cosn2πk+isinn2πk。
将多项式
A
,
B
A,B
A,B 的项数补到 2 的整数次幂,设项数为
n
n
n,采用分治法快速求出
A
,
B
A,B
A,B 代入
ω
n
0
,
ω
n
1
,
…
,
ω
n
n
−
1
\omega_{n}^0, \omega^1_n, \dots, \omega^{n - 1}_{n}
ωn0,ωn1,…,ωnn−1 的点值,将点值相乘再通过类似的过程还原回多项式,即可快速求出多项式
A
×
B
A \times B
A×B,时间复杂度
O
(
n
log
n
)
\mathcal O(n \log n)
O(nlogn)。
DFT
即离散傅里叶变换。
对于多项式
A
(
x
)
=
∑
k
=
0
n
−
1
a
k
x
k
=
∑
k
=
0
n
2
−
1
(
a
2
k
x
2
k
+
a
2
k
+
1
x
2
k
+
1
)
=
∑
k
=
0
n
2
−
1
a
2
k
(
x
2
)
k
+
x
∑
k
=
0
n
2
−
1
a
2
k
+
1
(
x
2
)
k
A(x) = \sum\limits_{k = 0}^{n - 1}a_k x^k = \sum \limits_{k = 0}^{\frac{n}{2} - 1}(a_{2k}x^{2k} + a_{2k + 1}x^{2k + 1}) = \sum \limits_{k = 0}^{\frac{n}{2} - 1}a_{2k}(x^{2})^k + x\sum\limits_{k = 0}^{\frac{n}{2} - 1}a_{2k+1}(x^2)^k
A(x)=k=0∑n−1akxk=k=0∑2n−1(a2kx2k+a2k+1x2k+1)=k=0∑2n−1a2k(x2)k+xk=0∑2n−1a2k+1(x2)k
设
A
1
(
x
)
=
∑
k
=
0
n
2
−
1
a
2
k
x
k
,
A
2
(
x
)
=
∑
i
=
0
n
2
−
1
a
2
k
+
1
x
k
A_1(x) = \sum \limits_{k = 0}^{\frac{n}{2} - 1}a_{2k}x^k, A_2(x) = \sum\limits_{i = 0}^{\frac{n}{2}-1}a_{2k + 1}x^k
A1(x)=k=0∑2n−1a2kxk,A2(x)=i=0∑2n−1a2k+1xk,若已求得
A
1
(
ω
n
2
k
)
,
A
2
(
ω
n
2
k
)
,
0
≤
k
≤
n
2
−
1
A_1(\omega_\frac{n}{2}^k), A_2(\omega^{k}_{\frac{n}{2}}), 0 \le k \le \frac{n}{2} - 1
A1(ω2nk),A2(ω2nk),0≤k≤2n−1,则
A
(
ω
n
k
)
=
A
1
(
ω
n
2
k
)
+
w
n
k
A
2
(
w
n
2
k
)
=
A
1
(
ω
n
2
k
)
+
w
n
k
A
2
(
ω
n
2
k
)
A
(
ω
n
k
+
n
2
)
=
A
1
(
ω
n
2
k
)
−
w
n
k
+
n
2
A
2
(
w
n
2
k
)
=
A
1
(
ω
n
2
k
)
−
w
n
k
A
2
(
ω
n
2
k
)
A(\omega_n^k) = A_1(\omega^{2k}_{n}) + w_{n}^{k}A_2(w_{n}^{2k}) = A_1(\omega^{k}_{\frac{n}{2}}) + w_{n}^{k}A_2(\omega^{k}_{\frac{n}{2}}) \\ A(\omega_n^{k + \frac{n}{2}}) = A_1(\omega^{2k}_{n}) - w_{n}^{k+\frac{n}{2}}A_2(w_{n}^{2k}) = A_1(\omega^{k}_{\frac{n}{2}}) -w_{n}^{k}A_2(\omega^{k}_{\frac{n}{2}}) \\
A(ωnk)=A1(ωn2k)+wnkA2(wn2k)=A1(ω2nk)+wnkA2(ω2nk)A(ωnk+2n)=A1(ωn2k)−wnk+2nA2(wn2k)=A1(ω2nk)−wnkA2(ω2nk)
不断分治下去,此时我们便求得了
A
(
ω
n
k
)
,
0
≤
k
≤
n
−
1
A(\omega^{k}_n), 0 \le k \le n - 1
A(ωnk),0≤k≤n−1。
IDFT
即逆离散傅里叶变换,由之前的叙述我们有:
[
(
ω
n
0
)
0
(
ω
n
0
)
1
⋯
(
ω
n
0
)
n
−
1
(
ω
n
1
)
0
(
ω
n
1
)
1
⋯
(
ω
n
1
)
n
−
1
⋮
⋮
⋱
⋮
(
ω
n
n
−
1
)
0
(
ω
n
n
−
1
)
1
⋯
(
ω
n
n
−
1
)
n
−
1
]
[
a
0
a
1
⋮
a
n
−
1
]
=
[
A
(
ω
n
0
)
A
(
ω
n
1
)
⋮
A
(
ω
n
n
−
1
)
]
\left[
对矩阵求逆,可得:
[
a
0
a
1
⋮
a
n
−
1
]
=
1
n
[
(
ω
n
−
0
)
0
(
ω
n
−
0
)
1
⋯
(
ω
n
−
0
)
n
−
1
(
ω
n
−
1
)
)
0
(
ω
n
−
1
)
1
⋯
(
ω
n
−
1
)
n
−
1
⋮
⋮
⋱
⋮
(
ω
n
−
(
n
−
1
)
)
0
(
ω
n
−
(
n
−
1
)
)
1
⋯
(
ω
n
−
(
n
−
1
)
)
n
−
1
]
[
A
(
ω
n
0
)
A
(
ω
n
1
)
⋮
A
(
ω
n
n
−
1
)
]
\left[
不难发现,将
ω
n
k
\omega_n^k
ωnk 替换成
ω
n
−
k
\omega_n^{-k}
ωn−k 再做一遍 DFT,最后将结果除以
n
n
n 即可实现 IDFT。
位逆序变换
为了减小递归实现带来的常数,我们考虑直接得到递归最底层的排列顺序,再逐层向上合并。
不难证明,设
n
=
2
m
n = 2^m
n=2m,则系数
a
i
a_i
ai 递归到最底层时的下标恰好为
i
i
i 在
m
m
m 位二进制表示下的对称翻转,我们称这个变换为位逆序变换(蝴蝶变换)。
设
r
e
v
[
i
]
rev[i]
rev[i] 表示系数
a
i
a_i
ai 递归到最底层时的下标,则显然有递推式
int m =0, _n = na + nb;//相乘的两个多项式最高次数分别为 na,nbfor(n =1; n <= _n; n <<=1)++m;for(int i =1; i < n;++i)
rev[i]=(rev[i >>1]>>1)|((i &1)<<(m -1));
1
2
3
4
5
最终我们得到了 FFT 的迭代实现。
typedeflongdouble ld;typedef complex<ld> com;const ld pi =acos(-1.0);inlinevoidDFT(vector<com>&a,int opt){int n = a.size();for(int i =0; i < n;++i)if(i < rev[i])
std::swap(a[i], a[rev[i]]);for(int k =1; k < n; k <<=1){
com w(cos(pi / k), opt *sin(pi / k));;for(int i =0; i < n; i += k <<1){
com res(1.0,0.0);for(int j =0; j < k;++j){
com u = a[i + j],
v = res * a[i + j + k];
a[i + j]= u + v;
a[i + j + k]= u - v;
res = res * w;}}}if(opt ==-1){for(int i =0; i < n;++i)
a[i]/= n;}}
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
NTT
即快速数论变换。
在模质数
P
P
P 意义下,原根
g
g
g 具有单位根的性质:
(
g
k
)
P
−
1
≡
1
(
m
o
d
P
)
,
g
P
−
1
2
≡
−
1
(
m
o
d
P
)
(g^k)^{P - 1} \equiv 1(\mod P), g^{\frac{P - 1}{2}}\equiv -1(\mod P)
(gk)P−1≡1(modP),g2P−1≡−1(modP)
若
P
=
2
x
a
+
1
P = 2^xa +1
P=2xa+1,设
n
=
2
m
n = 2^m
n=2m,用
g
2
x
−
m
a
g^{2^{x - m}a}
g2x−ma 替换
ω
n
\omega_n
ωn 即可实现 NTT。
常见的
P
P
P 有:
P
=
1004535809
=
479
×
2
21
+
1
,
g
=
3
P
=
998244353
=
7
×
17
×
2
23
+
1
,
g
=
3
P = 1004535809 = 479 \times 2 ^ {21} + 1, g = 3 \\P = 998244353 = 7 \times 17 \times 2^{23} + 1, g = 3
P=1004535809=479×221+1,g=3P=998244353=7×17×223+1,g=3
实现时可以预处理
g
2
x
−
m
a
g^{2^{x - m}a}
g2x−ma 的幂次,减小常数。
constint mod =998244353;constint inv3 =332748118;inlinevoidDFT(vector<int>&a,int opt){int n = a.size(), g = opt ==1?3: inv3;for(int i =0; i < n;++i)if(i < rev[i])
std::swap(a[i], a[rev[i]]);for(int k =1; k < n; k <<=1){int w =quick_pow(g,(mod -1)/(k <<1));
tw[0]=1;for(int j =1; j < k;++j)
tw[j]=1ll* tw[j -1]* w % mod;for(int i =0; i < n; i += k <<1){for(int j =0; j < k;++j){int u = a[i + j],
v =1ll* tw[j]* a[i + j + k]% mod;add(a[i + j]= u, v);dec(a[i + j + k]= u, v);}}}if(opt ==-1){int inv_n =quick_pow(n, mod -2);for(int i =0; i < n;++i)
a[i]=1ll* a[i]* inv_n % mod;}}