NTT类似于FFT,只是把单位复数根改成了具有特殊性质的数,当然是在mo意义下的。
假设是mo一个数p。
p一定要是一个质数,并且p-1 = 2^x*y,2^x次方还要比较大。
原根:
对于一个质数p,它一定有原根g。
满足g^x(mod p)意义下互不相同(0<=x < < <script type="math/tex" id="MathJax-Element-1"><</script>p-1)
对于原根的判定,可以判断是否存在一个g^x=1(mod p)(1<=x < < <script type="math/tex" id="MathJax-Element-2"><</script>p-1),那么g就不是原根。
原根只能暴力找,但是由于原根都比较小,所以可以很快找到。
像1004535809、998244353的原根是3。
w的取用:
原来的 win w n i 对应 gi∗(mo−1)n g i ∗ ( m o − 1 ) n 。
为什么呢?
对于单位复数根有两个核心要求(满足它们就行了):
1.互不相同
2.
win∗wjn=w(i+j) mod nn
w
n
i
∗
w
n
j
=
w
n
(
i
+
j
)
m
o
d
n
因为g是原根,所以第一个性质成立。
对于第二个性质,显然有:
gi∗(mo−1)n∗gj∗(mo−1)n
g
i
∗
(
m
o
−
1
)
n
∗
g
j
∗
(
m
o
−
1
)
n
=(gi)(mo−1)n∗(gj)(mo−1)n
=
(
g
i
)
(
m
o
−
1
)
n
∗
(
g
j
)
(
m
o
−
1
)
n
=(gi+j)(mo−1)n
=
(
g
i
+
j
)
(
m
o
−
1
)
n
又有:
(gn)(mo−1)n
(
g
n
)
(
m
o
−
1
)
n
=gmo−1
=
g
m
o
−
1
=1
=
1
所以:
=(gi+j)(mo−1)n
=
(
g
i
+
j
)
(
m
o
−
1
)
n
=(g(i+j) mod n)(mo−1)n
=
(
g
(
i
+
j
)
m
o
d
n
)
(
m
o
−
1
)
n
所以这些数是可以完全替代单位复数根了。
NTT大概就是这样。
3(多)模数NTT:
有些题目(51nod上的)非常恶意。
多项式每一项的系数可能特别大,比如说10^22。
这时候你FFT用long double 精度炸了, 直接NTT也不行。
于是可以取3个NTT模数,最后中国剩余定理搞一下就行了。
多项式求逆:
对于一个多项式 A A .
求,满足 A∗B=1(mod xn) A ∗ B = 1 ( m o d x n )
这个东西可以弄到大数除法里去,可惜只能是整除,有些题也需要它。
思路在于分治。
假设已经求出了 B′ B ′ ,满足 A∗B′=1(mod xn/2) A ∗ B ′ = 1 ( m o d x n / 2 )
显然有:
A∗(B−B′)=0(mod xn/2)
A
∗
(
B
−
B
′
)
=
0
(
m
o
d
x
n
/
2
)
B−B′=0(mod xn/2)
B
−
B
′
=
0
(
m
o
d
x
n
/
2
)
两边同时平方,模数也平方,得:
B2−2BB′+B′2=0(mod xn)
B
2
−
2
B
B
′
+
B
′
2
=
0
(
m
o
d
x
n
)
两边同时乘A,得:
B−2B′+AB′2=0
B
−
2
B
′
+
A
B
′
2
=
0
B=B′(2−AB′)
B
=
B
′
(
2
−
A
B
′
)
乘法用FFT加速。
复杂度是 T(n)=n log n+T(n/2)≈n log n T ( n ) = n l o g n + T ( n / 2 ) ≈ n l o g n
代码:
NTT:
void dft(ll *a, int n) {
ff(i, 0, n) {
int p = i, q = 0;
ff(j, 0, tp) q = q * 2 + p % 2, p /= 2;
if(q > i) swap(a[q], a[i]);
}
for(int m = 2; m <= n; m *= 2) {
int h = m / 2;
ff(i, 0, h) {
int W = w[i * (n / m)];
for(int j = i; j < n; j += m) {
int k = j + h;
ll u = a[j], v = a[k] * W % mo;
a[j] = (u + v) % mo; a[k] = (u - v + mo) % mo;
}
}
}
}
void fft(ll *a, ll *b, int n) {
ll rev = ksm(3, (mo - 1) / n);
w[0] = 1; fo(i, 1, n) w[i] = w[i - 1] * rev % mo;
dft(a, n); dft(b, n);
ff(i, 0, n) a[i] = a[i] * b[i] % mo;
fo(i, 0, n / 2) swap(w[i], w[n - i]);
dft(a, n); ll ni = ksm(n, mo - 2);
ff(i, 0, n) a[i] = a[i] * ni % mo;
}