NTT简单总结

NTT简单总结

NTT是一个玄学的东西,它通过数论达到和FFT一样的效果(甚至还要快得多)。

NTT的原理其实是将FFT的$w^{k}_{n}$通过另外一个东西代替,从而将FFT中极其之慢的(而且精度爆炸的)浮点数运算更改为整数运算。

在讲NTT之前,先来了解一下数学方面的内容。

数学部分

若$(a,p)$=1,且$p>1$,对于$a^{n}\equiv1(mod p)$最小的$n$称为$a$模$p$的阶,记为$\delta_{p}(a)$

原根

设正整数$p$,整数$a$,若$\delta_{p}(a)=\phi(p)$则称$a$是模$p$的一个原根

原根有一些有趣的性质,而且跟我们在FFT中用到的单位根用到的性质一样!别问我怎么证

为了避免吐槽,还是放个大佬证明

好的,我们终于将浮点数运算转换到了整数域运算。

所以我们直接将FFT代码中的单位根全部换成原根就阔以了。

(不会FFT的请左转百度或右转我的博客

 代码部分

话说这种算法都应该背模板

#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e7+5,P=998244353,P1=3,P2=332748118;
int lena,lenb,n=1,lim,r[N];
ll a[N],b[N];
ll rpow(ll x,ll y){//要手打
	ll res=1;
	while(y){
		if(y&1)res=(res*x)%P;
		x=(x*x)%P;
		y>>=1;
	}
	return res%P;
}
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void NTT(ll *A,int tp){
    for(int i=0;i<n;i++)if(i<r[i])swap(A[i],A[r[i]]);
    for(int i=1;i<n;i<<=1){
        ll W=rpow(tp?P1:P2,(P-1)/(i<<1));//替换成原根
        for(int j=i<<1,k=0;k<n;k+=j){
            ll w=1;
            for(int l=0;l<i;l++,w=(w*W)%P){//注意模数
                int x=A[k+l],y=w*A[k+i+l]%P;
                A[k+l]=(x+y)%P;
                A[k+i+l]=(x-y+P)%P;
            }
        }
    }
}
int main(){
    lena=read();lenb=read();
    while(n<=lena+lenb)n<<=1,lim++;
    for(int i=0;i<=lena;i++)a[i]=(read()+P)%P;
    for(int i=0;i<=lenb;i++)b[i]=(read()+P)%P;
    for(int i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(lim-1));
    NTT(a,1);
    NTT(b,1);
    for(int i=0;i<=n;i++)a[i]=(a[i]*b[i])%P;
    NTT(a,0);
    ll inv=rpow(n,P-2);
    for(int i=0;i<=lena+lenb;i++)printf("%d ",(a[i]*inv)%P);
} 

  

实测洛谷P3803用FFT 3.14s,NTT 1.76s(不吸氧)

(上为NTT,下为FFT)

猜你喜欢

转载自www.cnblogs.com/Evan704/p/11409421.html
NTT