版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sizeof_you/article/details/84557728
有时候会被卡精度?所以可能会有模数,有了模数以后就需要模数的原根。
原根是什么?(留坑待填)
有很多种解决方法
特殊模数
,可以直接暴力求原根
,用
代替单位复数根
一般模数
- 三模数法(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(a[0][i]*mod2%M,qpow(mod2,mod1-2,mod1),M))%=M;
(tmp+=mul(a[1][i]*mod1%M,qpow(mod1,mod2-2,mod2),M))%=M;
a[1][i]=tmp;
}
for(int i=0;i<limit;i++){//x=(c3-C)/M mod mod3
LL tmp=(a[2][i]-a[1][i]%mod3+mod3)%mod3*qpow(M%mod3,mod3-2,mod3)%mod3;
ans[i]=(M%p*tmp%p+a[1][i]%p)%p;
}
}
inline void solve(int x,int MOD){
NTT(a[x],1,MOD); NTT(b[x],1,MOD);
for(int i=0;i<limit;i++) a[x][i]=a[x][i]*b[x][i]%MOD;
NTT(a[x],-1,MOD);
}
int main(){
n=rd(); m=rd(); p=rd();
for(int i=0;i<=n;i++){
int x=rd();
for(int j=0;j<3;j++) a[j][i]=x%p;
}
for(int i=0;i<=m;i++){
int x=rd();
for(int j=0;j<3;j++) b[j][i]=x%p;
}
while(limit<=n+m) limit<<=1,++l;
for(int i=0;i<limit;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
solve(0,mod1); solve(1,mod2); solve(2,mod3);
CRT();
for(int i=0;i<=n+m;i++) printf("%lld ",ans[i]);
return 0;
}
-
拆系数法(7次DFT)
将两个多项式系数拆开,然后发现可以分成三部分计算,不很常用而且精度掉的厉害,这里先不做详细解释 -
MTT(4次DFT)
奇技淫巧,话说三模数NTT卡卡常多预处理一些应该都能过去,不行就开O2,跑的蛮快的(反正省选也开O2),这个东西先放着,以后闲得没事再来看