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。
w的取用:
原来的winwni对应gi∗(mo−1)ngi∗(mo−1)n。
为什么呢?
对于单位复数根有两个核心要求(满足它们就行了):
1.互不相同
2.win∗wjn=w(i+j) mod nnwni∗wnj=wn(i+j) mod n
因为g是原根,所以第一个性质成立。
对于第二个性质,显然有:
gi∗(mo−1)n∗gj∗(mo−1)ngi∗(mo−1)n∗gj∗(mo−1)n
=(gi)(mo−1)n∗(gj)(mo−1)n=(gi)(mo−1)n∗(gj)(mo−1)n
=(gi+j)(mo−1)n=(gi+j)(mo−1)n
又有:
(gn)(mo−1)n(gn)(mo−1)n
=gmo−1=gmo−1
=1=1
所以:
=(gi+j)(mo−1)n=(gi+j)(mo−1)n
=(g(i+j) mod n)(mo−1)n=(g(i+j) mod n)(mo−1)n
所以这些数是可以完全替代单位复数根了。
NTT大概就是这样。
3(多)模数NTT:
有些题目(51nod上的)非常恶意。
多项式每一项的系数可能特别大,比如说10^22。
这时候你FFT用long double 精度炸了, 直接NTT也不行。
于是可以取3个NTT模数,最后中国剩余定理搞一下就行了。
多项式求逆:
对于一个多项式AA.
求,满足A∗B=1(mod xn)A∗B=1(mod xn)
这个东西可以弄到大数除法里去,可惜只能是整除,有些题也需要它。
思路在于分治。
假设已经求出了B′B′,满足A∗B′=1(mod xn/2)A∗B′=1(mod xn/2)
显然有:
A∗(B−B′)=0(mod xn/2)A∗(B−B′)=0(mod xn/2)
B−B′=0(mod xn/2)B−B′=0(mod xn/2)
两边同时平方,模数也平方,得:
B2−2BB′+B′2=0(mod xn)B2−2BB′+B′2=0(mod xn)
两边同时乘A,得:
B−2B′+AB′2=0B−2B′+AB′2=0
B=B′(2−AB′)B=B′(2−AB′)
乘法用FFT加速。
复杂度是T(n)=n log n+T(n/2)≈n log nT(n)=n log n+T(n/2)≈n log 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;
}