开始补EC PK赛的题目,在此之前决定先完善多项式全家桶的学习任务。
FFT
在 O ( l o g N ) O(logN) O(logN)的时间完成点值、系数的多项式表示,在 O ( N ) O(N) O(N)的时间完成多项式乘积。
FFT在之前学习过:[多项式全家桶] 快速傅立叶变换(FFT)
再推荐一篇博客:FFT\NTT总结
在学习别的知识点之前又自己结合原理撸了一遍fft代码,贴一下符合自己习惯(比如从0开始的计数)的代码:
//逆fft取op==-1是因将范德蒙德行列式的逆时每一项取共轭再除以n
void fft(COM *a, int op){
for(int i = 0; i < N; i++) if(i < inv[i]) swap(a[inv[i]], a[i]);
for(int i = wei - 1; i >= 0; i--){
//d表示第i层每一小段的长度
int d = 1 << (wei - i);
//wn是当前层下使用的单位根
COM wn = {cos(2 * pi / d), op * sin(2 * pi / d)};
//j遍历第i层的每段开头,更新每个d小段
for(int j = 0; j < N; j += d){
//在一个小段中新设一个量w进行更新操作
COM w = {1, 0};
for(int k = 0; k < d / 2; k++){
COM x, y;
x = a[j + k]; y = a[j + k + d / 2] * w;
a[j + k] = x + y; a[j + k + d / 2] = x - y;
w = w * wn;
}
}
}
}
注意,如果对多项式A和B进行FFT求乘积时,A、B数组的大小都要开至maxn << 2!!
NTT
在此之前,需要学习一下原根
的知识点。
参考博客:HolseLee
定义阶
为:设
m
>
1
,
g
c
d
(
a
,
m
)
=
1
,
m>1,gcd(a,m)=1,
m>1,gcd(a,m)=1,那么使得
a
r
≡
1
(
m
o
d
a^r≡1(mod
ar≡1(mod
m
)
m)
m)成立的最小正整数称为
a
a
a对模
m
m
m的阶,记为
δ
m
(
a
)
\delta_m(a)
δm(a)
易知阶有两个相关定理:
- 若满足 m > 1 , g c d ( a , m ) = 1 m>1,gcd(a,m)=1 m>1,gcd(a,m)=1,并且 a n ≡ 1 ( m o d a^n≡1(mod an≡1(mod m ) m) m),则有 δ m ( a ) \delta_m(a) δm(a) | n . n. n.
- δ m ( a ) \delta_m(a) δm(a) | ϕ ( m ) . \phi(m). ϕ(m).
进而,定义原根
:设
a
a
a是整数,
m
m
m是正整数,如果
δ
m
(
a
)
=
ϕ
(
m
)
\delta_m(a) =\phi(m)
δm(a)=ϕ(m),那么称
a
a
a为
m
m
m的一个原根。
原根有三个相关定理:
- 一个正整数
m
m
m有原根的充要条件是
m
=
2
,
4
,
p
e
,
2
p
e
m=2,4,p^e,2p^e
m=2,4,pe,2pe,其中
p
p
p是奇素数,
e
e
e是正整数。
证明略
- 若
p
p
p是
m
m
m的一个原根,则
p
,
p
2
,
p
2
,
…
,
p
ϕ
(
m
)
p,p^2,p^2,\dots,p^{\phi(m)}
p,p2,p2,…,pϕ(m)各数对
m
m
m取模的最小剩余就是小于
m
m
m且与
m
m
m互素的
ϕ
(
m
)
\phi(m)
ϕ(m)个数的一个排列。
证明:
易知任意两个元素均不相同,因而接下来只需要证明每个元素都与 m m m互素。
由 p ϕ ( m ) ≡ 1 ( m o d m ) p^{\phi(m)}≡1(mod\space m) pϕ(m)≡1(mod m)可知 ( p i ) ϕ ( m ) ≡ 1 ( m o d m ) (p^i)^{\phi(m)}≡1(mod \space m) (pi)ϕ(m)≡1(mod m)因而我们只需要证明如果某个数 a a a对 m m m的阶存在,那么就有 g c d ( a , m ) = 1 gcd(a,m)=1 gcd(a,m)=1.为此,我们假设 a i ≡ 1 ( m o d m ) a^i≡1(mod \space m) ai≡1(mod m)且由 g c d ( a , m ) = d gcd(a,m)=d gcd(a,m)=d.所以有 a i = k m + 1 a^i=km+1 ai=km+1
其中 a i a^i ai和 k m km km均可以被 d d d整除,因而1也必须被d整除,故 d ∣ 1 d \mid1 d∣1即 d = 1. d=1. d=1. - 对每一个正整数
m
m
m都有
ϕ
(
ϕ
(
m
)
)
\phi(\phi(m))
ϕ(ϕ(m))个原根,特别地,素数
p
p
p有
ϕ
(
p
−
1
)
\phi(p-1)
ϕ(p−1)个原根。
证明:
若 p p p是 m m m的一个原根,那么由上述定理其既约剩余系取遍小于 m m m且与 m m m互素的所有数。在 1 、 2 、 … 、 ϕ ( m ) 1、2、\dots、\phi(m) 1、2、…、ϕ(m)中只有那些与 ϕ ( m ) \phi(m) ϕ(m)互素的幂次才可以成为原根,这是因为那些与 ϕ ( m ) \phi(m) ϕ(m)不互素的元素产生的 ϕ ( m ) \phi(m) ϕ(m)个元素是某些元素集合的 d d d次重复,其中 d = g c d ( p i , ϕ ( m ) ) . d= gcd(p^i,\phi(m)). d=gcd(pi,ϕ(m)).
原根的求法?
首先将
ϕ
(
m
)
\phi(m)
ϕ(m)做标准素数分解:
ϕ
(
m
)
=
p
1
e
1
∗
p
2
e
2
∗
⋯
∗
p
k
e
k
\phi(m)=p_1^{e_1}*p_2^{e_2}*\dots*p_k^{e_k}
ϕ(m)=p1e1∗p2e2∗⋯∗pkek
然后枚举
g
g
g,若恒满足
g
ϕ
(
m
)
p
i
≠
1
(
m
o
d
m
)
,
i
=
1
,
2
,
…
,
k
g^{\frac{\phi(m)}{p_i}}≠1(mod\space m),\space i=1,2,\dots,k
gpiϕ(m)=1(mod m), i=1,2,…,k
则
g
g
g是
m
m
m的一个原根。
证明可以从阶的第二个定理出发。因为 δ m ( a ) \delta m(a) δm(a)总是 ϕ ( m ) \phi(m) ϕ(m)的一个因子,所以若 δ m ( a ) \delta m(a) δm(a)小于 ϕ ( m ) \phi(m) ϕ(m),则 δ m ( a ) \delta m(a) δm(a)一定会小等于某一个 ϕ ( m ) p i \frac{\phi(m)}{p_i} piϕ(m),从而 g ϕ ( m ) p i g^{\frac{\phi(m)}{p_i}} gpiϕ(m)模 m m m的值一定为1.
下面正式学习一下NTT(快速数论变换)
参考博客:快速数论变换(NTT)小结
类似于FFT中我们寻找的 w n w_n wn,我们在模 M M M下利用原根做循环。
因为我们采取二分递归的办法解决点值、系数转化问题,所以我们希望找一些具有优良的特殊性质的素数
M
=
k
∗
2
r
+
1
M=k*2^r+1
M=k∗2r+1作为模数。(参考:FFT用到的各种素数)
比如:
119
<
<
23
+
1
119<<23+1
119<<23+1可以处理
1
e
6
1e6
1e6的数据,
17
<
<
27
+
1
17<<27+1
17<<27+1平方后不会爆longlong. 这两个数字对应的原根均为3.
之后,我们构造循环:
w
n
=
p
m
−
1
n
w_n=p^{\frac{m-1}{n}}
wn=pnm−1
由原根性质,
w
n
,
w
n
2
,
…
,
w
n
n
w_n,w_n^2,\dots,w_n^n
wn,wn2,…,wnn构成一个
n
n
n阶循环群,满足复数
w
n
=
e
2
π
n
w_n=e^{\frac{2 \pi }{n}}
wn=en2π的对应性质。
所以在求做INNT的时候用
p
p
p的逆作为逆矩阵的元素,最后在输出答案的时候除以
n
n
n,即乘上
n
n
n的逆。
核心代码
与FFT类似,仅需要在必要处取模&更改对应的循环因子
w
n
w_n
wn即可。
暂且先复习FFT和学习NTT,之后更多的多项式算法日后再学。