仙人NTT的入门

仙人NTT的入门

更详细的NTT在这里

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

那么什么是原根呢?
定义 p p 的原根为满足gϕ(p)1(modp)的整数 g g ,其中,ϕ(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,那么 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次单位复数根的平方的集合就是 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 <bits/stdc++.h>
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;i<n;++i)
        if(i<r[i]) swap(now[i],now[r[i]]);
    for(int i=1;i<n;i<<=1) {
        int gn=ksm(G,(mod-1)/(i<<1));
        for(int j=0;j<n;j+=(i<<1)) {
            int x,y,g=1;
            for(int k=0;k<i;++k,g=1ll*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;i<n;++i) now[i]=1ll*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<n;++i)
        r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    ntt(a,1);ntt(b,1);
    for(int i=0;i<n;++i) a[i]=1ll*a[i]*b[i]%mod;
    ntt(a,-1);
    for(int i=0;i<=m;++i)
        printf("%d ",a[i]);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值