仙人NTT的入门

仙人NTT的入门

更详细的NTT在这里

在快速傅里叶变换中,我们利用 wn w n 单位复数根实现了消去引理和折半引理
但是由于复数运算的关系,导致精度问题,使人十分捉鸡
那么,有没有什么整数也满足消去引理和折半引理来代替 wn w n 单位复数根呢?
这就是所谓的原根

那么什么是原根呢?
定义 p p 的原根为满足 gϕ(p)1(modp) g ϕ ( p ) ≡ 1 ( mod p ) 的整数 g g ,其中, ϕ(p) ϕ ( p ) 为 满 足 条 件 的 最 小 指 数
并且要求 gi≢gj(modp) g i ≢ g j ( mod p )
那么我们用 gi g i 生成的横坐标就不会相同,以此来生成点集表示法

现在我们用 gϕ(p)n g ϕ ( p ) n 来代替 e2πin e 2 π i n
因为我们想利用整数进行计算,那么 ϕ(p)n ϕ ( p ) n 应该为整数,因为当 p p 为质数时, ϕ(p)=p1 ϕ ( p ) = p − 1 ,那么 p10(mod2q),n2q p − 1 ≡ 0 ( mod 2 q ) , n ≤ 2 q

消去引理: wdkdn=wkn w d n d k = w n k
* 证明: wdkdn=gdkϕ(p)dn=gkϕ(p)n=wkn w d n d k = g d k ϕ ( p ) d n = g k ϕ ( p ) n = w n k

折半引理: n n 为偶数,则 n n 次单位复数根的平方的集合就是 n2 n 2 次单位复数根的集合,特别的,每个 n2 n 2 次单位复数根出现两次
* 证明: (wkn)2=wkn2,k[0,n1] ( w n k ) 2 = w n 2 k , k ∈ [ 0 , n − 1 ] [消去引理]

非常nice

所以就和FFT一样一样的啦!

板子!
多项式乘法

#include 
using namespace std;
const int N = 3000010;
const int G=3,mod=(119<<23)+1;
int n,m,l;
int a[N],b[N],r[N];
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while((ch>'9' || ch<'0') && ch!='-') ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    return ans*flag;
}
int ksm(int a,int b) {
    int ans=1;
    while(b) {
        if(b&1) ans=1ll*ans*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ans;
}
void ntt(int *now,int f) {
    for(int i=0;iif(ifor(int i=1;i1) {
        int gn=ksm(G,(mod-1)/(i<<1));
        for(int j=0;j1)) {
            int x,y,g=1;
            for(int k=0;k1ll*g*gn%mod) {
                x=now[j+k],y=1ll*g*now[j+k+i]%mod;
                now[j+k]=(x+y)%mod;
                now[j+k+i]=(x-y+mod)%mod;
            }
        }
    }
    if(f!=1) {
        int ny=ksm(n,mod-2);
        reverse(now+1,now+n);
        for(int i=0;i1ll*now[i]*ny%mod;
    }
}
int main() {
    n=read();m=read();
    for(int i=0;i<=n;++i) a[i]=read();
    for(int i=0;i<=m;++i) b[i]=read();
    m+=n;
    for(n=1;n<=m;n<<=1) ++l;
    for(int i=0;i>1]>>1)|((i&1)<<(l-1));
    ntt(a,1);ntt(b,1);
    for(int i=0;i1ll*a[i]*b[i]%mod;
    ntt(a,-1);
    for(int i=0;i<=m;++i)
        printf("%d ",a[i]);
    return 0;
}

你可能感兴趣的:(【数学】,【数学】快速数论变换)