NTT
嗷, 很简单。
FFT F F T 之所以能加速,是由于有主n次单位根 wn=e2πin w n = e 2 π i n ,的那些很好的性质。而在自然数域,模 P P 意义下,可以把 换成 gP−1n g P − 1 n , g g 是 的原根,可以发现那些性质是类似的。逆变换也是把 gP−1n g P − 1 n 换成 g−P−1n g − P − 1 n ,然后最后都除以 n n 。
要能NTT,需要满足模数 是质数且
P−1
P
−
1
是
n
n
(2的次幂) 的倍数。所以 一般应当是
常见的模数:
r * 2 ^ k + 1 | r | k | g |
---|---|---|---|
3 | 1 | 1 | 2 |
5 | 1 | 2 | 2 |
17 | 1 | 4 | 3 |
97 | 3 | 5 | 5 |
193 | 3 | 6 | 5 |
257 | 1 | 8 | 3 |
7681 | 15 | 9 | 17 |
12289 | 3 | 12 | 11 |
40961 | 5 | 13 | 3 |
65537 | 1 | 16 | 3 |
786433 | 3 | 18 | 10 |
5767169 | 11 | 19 | 3 |
7340033 | 7 | 20 | 3 |
23068673 | 11 | 21 | 3 |
104857601 | 25 | 22 | 3 |
167772161 | 5 | 25 | 3 |
469762049 | 7 | 26 | 3 |
998244353 | 119 | 23 | 3 |
1004535809 | 479 | 21 | 3 |
2013265921 | 15 | 27 | 31 |
2281701377 | 17 | 27 | 3 |
3221225473 | 3 | 30 | 5 |
75161927681 | 35 | 31 | 3 |
77309411329 | 9 | 33 | 7 |
206158430209 | 3 | 36 | 22 |
2061584302081 | 15 | 37 | 7 |
板子 (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;
}