NTT(快速数论变换)和多项式求逆学习小记

本文详细介绍了NTT(Number Theoretic Transform)的基本原理及其在多项式运算中的应用,包括如何选取合适的模数、确定原根及利用NTT进行快速多项式乘法等。此外,还探讨了多项式求逆的问题及其实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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(mo1)ngi∗(mo−1)n

为什么呢?

对于单位复数根有两个核心要求(满足它们就行了):

1.互不相同
2.winwjn=w(i+j) mod nnwni∗wnj=wn(i+j) mod n

因为g是原根,所以第一个性质成立。

对于第二个性质,显然有:
gi(mo1)ngj(mo1)ngi∗(mo−1)n∗gj∗(mo−1)n
=(gi)(mo1)n(gj)(mo1)n=(gi)(mo−1)n∗(gj)(mo−1)n
=(gi+j)(mo1)n=(gi+j)(mo−1)n

又有:
(gn)(mo1)n(gn)(mo−1)n
=gmo1=gmo−1
=1=1

所以:
=(gi+j)(mo1)n=(gi+j)(mo−1)n
=(g(i+j) mod n)(mo1)n=(g(i+j) mod n)(mo−1)n

所以这些数是可以完全替代单位复数根了。

NTT大概就是这样。

3(多)模数NTT:

有些题目(51nod上的)非常恶意。

多项式每一项的系数可能特别大,比如说10^22。

这时候你FFT用long double 精度炸了, 直接NTT也不行。

于是可以取3个NTT模数,最后中国剩余定理搞一下就行了。

多项式求逆:

对于一个多项式AA.

B,满足AB=1(mod xn)A∗B=1(mod xn)

这个东西可以弄到大数除法里去,可惜只能是整除,有些题也需要它。

思路在于分治。

假设已经求出了BB′,满足AB=1(mod xn/2)A∗B′=1(mod xn/2)

显然有:
A(BB)=0(mod xn/2)A∗(B−B′)=0(mod xn/2)
BB=0(mod xn/2)B−B′=0(mod xn/2)
两边同时平方,模数也平方,得:
B22BB+B2=0(mod xn)B2−2BB′+B′2=0(mod xn)
两边同时乘A,得:
B2B+AB2=0B−2B′+AB′2=0
B=B(2AB)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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值