任意模數NTT(學習筆記)
阿新 • • 發佈:2018-12-02
有時候會被卡精度?所以可能會有模數,有了模數以後就需要模數的原根。
原根是什麼?(留坑待填)
有很多種解決方法
特殊模數
,可以直接暴力求原根
,用
代替單位複數根
一般模數
- 三模數法(9次DFT)
如果這個模數的原根不好求,而模數又很大,可以用三個模數
要求
常用 ,因為他們的原根都是
對這幾個模數分別做 ,然後用中國剩餘定理合併,然後對原模數取模即可
中國剩餘定理部分:
如果直接合並的話會爆 ,可以先合併兩個,再用奇技淫巧合並第三個
合併前兩個:
其中 表示 對 取模的逆元。
把上式化簡為
設
接下來很重要:求出 意義下的值:
這樣就可以算出右半部分的值 ,令 ,代入 得:
因為 ,所以 ,於是 就可直接計算。
做9次DFT,常數極大
有一道模板題luogu4245
直接用三模數法,程式碼如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LL long long
#define maxn 400005
using namespace std;
const LL mod1=998244353,mod2=1004535809,mod3=469762049,g=3;
const LL M=1LL*mod1*mod2;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
int n,m,p,rev[maxn],limit=1,l;
LL a[3][maxn],b[3][maxn],ans[maxn];
inline LL mul(LL x,int k,LL MOD){
LL ret=0;
while(k){
if(k&1) (ret+=x)%=MOD;
(x+=x)%=MOD; k>>=1;
} return ret%MOD;
}
inline LL qpow(LL x,int k,int MOD){
LL ret=1;
while(k){
if(k&1) ret=ret*x%MOD;
x=x*x%MOD; k>>=1;
} return ret%MOD;
}
inline void NTT(LL *F,int type,int MOD){
for(int i=0;i<limit;i++){
F[i]%=MOD;
if(i<rev[i]) swap(F[i],F[rev[i]]);
}
for(int mid=1;mid<limit;mid<<=1){
LL Wn=qpow(g,type==1?(MOD-1)/(mid<<1):(MOD-1-(MOD-1)/(mid<<1)),MOD);//!
for(int r=mid<<1,j=0;j<limit;j+=r){
LL w=1;
for(int k=0;k<mid;k++,w=w*Wn%MOD){
LL x=F[j+k],y=w*F[j+mid+k]%MOD;
F[j+k]=(x+y)%MOD; F[j+mid+k]=(x-y+MOD)%MOD;
}
}
}
if(type==-1){
LL INV=qpow(limit,MOD-2,MOD);//除以limit
for(int i=0;i<limit;i++) F[i]=F[i]*INV%MOD;
}
}
inline void CRT(){
for(int i=0;i<limit;i++){
LL tmp=0;
(tmp+=mul