快速数论变换(NTT)——学习笔记

NTT

嗷, 很简单。

FFT F F T 之所以能加速,是由于有主n次单位根 wn=e2πin w n = e 2 π i n ,的那些很好的性质。而在自然数域,模 P P 意义下,可以把 wn 换成 gP1n g P − 1 n g g P 的原根,可以发现那些性质是类似的。逆变换也是把 gP1n g P − 1 n 换成 gP1n g − P − 1 n ,然后最后都除以 n n

要能NTT,需要满足模数 P 是质数且 P1 P − 1 n n (2的次幂) 的倍数。所以 P 一般应当是

P=r2k+1(n2k) P = r ⋅ 2 k + 1 ( n ≤ 2 k )

常见的模数:

r * 2 ^ k + 1rkg
3112
5122
17143
97355
193365
257183
768115917
1228931211
409615133
655371163
78643331810
576716911193
73400337203
2306867311213
10485760125223
1677721615253
4697620497263
998244353119233
1004535809479213
2013265921152731
228170137717273
32212254733305
7516192768135313
773094113299337
20615843020933622
206158430208115377

板子 (51nod1028 大整数乘法):

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const LL P=998244353,_G=3;
const int maxn=270005;
int rev[maxn];
int Pow(LL a,int b){
  LL res=1;
  for(;b;b>>=1,a=a*a%P) if(b&1) res=res*a%P;
  return res;
}
void get_rev(int n){
  int t=0; while((1<<t)<n) t++;
  rev[0]=0; for(int i=1;i<=n-1;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<t-1);
}
void NTT(LL a[],int n,int k){
  for(int i=0;i<=n-1;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
  for(int m=2;m<=n;m<<=1){
    int wm=Pow(_G,(P-1)/m); if(k==-1) wm=Pow(wm,P-2);
    for(int i=0;i<=n-1;i+=m){
      LL w=1,t0,t1;
      for(int j=0;j<=m/2-1;j++,w=w*wm%P) t0=a[i+j], t1=w*a[i+j+m/2]%P, a[i+j]=(t0+t1)%P, a[i+j+m/2]=((t0-t1)%P+P)%P;
    }
  }
  if(k==-1){
    int t=Pow(n,P-2);
    for(int i=0;i<=n-1;i++) a[i]=(LL)a[i]*t%P;
  }
}
char st[maxn];
LL A[maxn],B[maxn];
int n;
int main(){
  scanf("%s",st); int len=strlen(st); n=len;
  for(int i=0;i<=len-1;i++) A[len-1-i]=st[i]-'0'; 
  scanf("%s",st); len=strlen(st); n=max(n,len);
  for(int i=0;i<=len-1;i++) B[len-1-i]=st[i]-'0';
  int _n=1; while(_n<n) _n<<=1; n=_n; n<<=1; get_rev(n); 
  NTT(A,n,1); NTT(B,n,1);
  for(int i=0;i<=n-1;i++) A[i]=A[i]*B[i]%P;
  NTT(A,n,-1);
  for(int i=0;i<=n-1;i++) A[i+1]+=A[i]/10, A[i]%=10;
  n--;
  while(A[n]/10) A[n+1]+=A[n]/10, A[n++]%=10;
  while(!A[n]&&n>0) n--;
  for(int i=n;i>=0;i--) putchar('0'+A[i]);
  return 0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值