NTT类似于FFT,只是把单位复数根改成了具有特殊性质的数,当然是在mo意义下的。
假设是mo一个数p。
p一定要是一个质数,并且p-1 = 2^x*y,2^x次方还要比较大。
对于一个质数p,它一定有原根g。
满足g^x(mod p)意义下互不相同(0<=x < < p-1)
对于原根的判定,可以判断是否存在一个g^x=1(mod p)(1<=x < < p-1),那么g就不是原根。
原根只能暴力找,但是由于原根都比较小,所以可以很快找到。
像1004535809、998244353的原根是3。
原来的 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大概就是这样。
有些题目(51nod上的)非常恶意。
多项式每一项的系数可能特别大,比如说10^22。
这时候你FFT用long double 精度炸了, 直接NTT也不行。
于是可以取3个NTT模数,最后中国剩余定理搞一下就行了。
对于一个多项式 A A .
求 B B ,满足 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;
}