任意模数NTT(三模数NTT)

前置知识


### 介绍 一般的NTT的模数$P$都要满足$P=r\times 2^k+1$。但如果不满足这个条件,那就不能直接用NTT了,需要用到任意模数NTT。

下面就来介绍一种任意模数NTT——三模数NTT。


三模数NTT

三模数NTT,即找三个模数,这三个模数满足上面NTT的三个条件,分别求出多项式乘法在这三个模数下的结果,然后利用中国剩余定理,求出在给定模数下的结果。

设给定模数为 p p p,自己找的三个模数为 m o d 1 , m o d 2 , m o d 3 mod1,mod2,mod3 mod1,mod2,mod3,则这三个模数还要满足

  • m o d 1 × m o d 2 × m o d 3 mod1\times mod2\times mod3 mod1×mod2×mod3大于结果多项式中的所有系数
  • m o d 1 × m o d 2 mod1\times mod2 mod1×mod2在long long范围内

对于结果多项式的系数 c c c,有:

f ( i ) = { c ≡ c 1 ( m o d m o d 1 ) c ≡ c 2 ( m o d m o d 2 ) c ≡ c 3 ( m o d m o d 3 ) f(i)= \left\{\begin{matrix} c\equiv c_1\pmod{mod1}\\ c\equiv c_2\pmod{mod2}\\ c\equiv c_3\pmod{mod3} \end{matrix}\right. f(i)= cc1(modmod1)cc2(modmod2)cc3(modmod3)

根据扩展中国剩余定理,由前两个方程可得
c 1 + m o d 1 × k 1 = c 2 + m o d 2 × k 2 c_1+mod1\times k_1=c_2+mod2\times k_2 c1+mod1×k1=c2+mod2×k2
k 1 × m o d 1 ≡ ( c 2 − c 1 ) ( m o d m o d 2 ) k_1\times mod1\equiv (c_2-c_1)\pmod{mod2} k1×mod1(c2c1)(modmod2)
k 1 ≡ ( c 2 − c 1 ) × m o d 1 − 1 ( m o d m o d 2 ) k_1\equiv (c_2-c_1)\times mod1^{-1}\pmod{mod2} k1(c2c1)×mod11(modmod2)

因为 c = c 1 + m o d 1 × k 1 c=c_1+mod1\times k_1 c=c1+mod1×k1,所以
c ≡ ( c 2 − c 1 ) × m o d 1 − 1 × m o d 1 + c 1 ( m o d m o d 1 × m o d 2 ) c\equiv(c_2-c_1)\times mod1^{-1}\times mod1+c_1\pmod{mod1\times mod2} c(c2c1)×mod11×mod1+c1(modmod1×mod2)

在这个式子中, m o d 1 − 1 mod1^{-1} mod11是指 m o d 1 mod1 mod1在模 m o d 2 mod2 mod2的意义下的逆元,所以 m o d 1 − 1 × m o d 1 mod1^{-1}\times mod1 mod11×mod1不一定为 1 1 1

m = ( c 2 − c 1 ) × m o d 1 − 1 × m o d 1 + c 1 m=(c_2-c_1)\times mod1^{-1}\times mod1+c_1 m=(c2c1)×mod11×mod1+c1,则上面的式子变为 c ≡ m ( m o d m o d 1 × m o d 2 ) c\equiv m\pmod{mod1\times mod2} cm(modmod1×mod2),将这个式子和第三个式子联立,同样可以得到

c ≡ ( c 3 − m ) × ( m o d 1 × m o d 2 ) − 1 × ( m o d 1 × m o d 2 ) + m ( m o d m o d 1 × m o d 2 × m o d 3 ) c\equiv (c_3-m)\times(mod1\times mod2)^{-1}\times (mod1\times mod2)+m\pmod{mod1\times mod2\times mod3} c(c3m)×(mod1×mod2)1×(mod1×mod2)+m(modmod1×mod2×mod3)

同样地, ( m o d 1 × m o d 2 ) − 1 (mod1\times mod2)^{-1} (mod1×mod2)1是在模 m o d 3 mod3 mod3的意义下的逆元。

要让 m o d 1 × m o d 2 mod1\times mod2 mod1×mod2不超过long long的范围,是防止溢出。而三个模数的乘积虽然会爆long long,但因为三个模数的乘积大于结果多项式中的所有系数,所以我们并不需要真的去模 m o d 1 × m o d 2 × m o d 3 mod1\times mod2\times mod3 mod1×mod2×mod3

这三个模数可以选择 998244353 , 1004535809 , 469762049 998244353,1004535809,469762049 998244353,1004535809,469762049,这三个质数的原根都为 3 3 3

整个过程中,总共有 9 9 9 D F T DFT DFT I D F T IDFT IDFT

时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn),常数有一点大。


例题

洛谷P4245 【模板】任意模数多项式乘法

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000000,g=3;
const ll mod1=998244353,mod2=1004535809,mod3=469762049;
int n,m,p,len=1;
ll w,wn,a[N+5],b[N+5],c1[N+5],c2[N+5],c3[N+5],x[N+5],y[N+5];
ll mi(ll t,ll v,ll mod){
	if(!v) return 1;
	ll re=mi(t,v/2,mod);
	re=re*re%mod;
	if(v&1) re=re*t%mod;
	return re;
}
void ch(ll *c,int l){
	for(int i=1,j=l/2,k;i<l-1;i++){
		if(i<j) swap(c[i],c[j]);
		k=l/2;
		while(j>=k){
			j-=k;k>>=1;
		}
		j+=k;
	}
}
void ntt(ll *c,int l,int fl,ll mod){
	ch(c,l);
	for(int i=2;i<=l;i<<=1){
		if(fl==1) wn=mi(g,(mod-1)/i,mod);
		else wn=mi(g,mod-1-(mod-1)/i,mod);
		for(int j=0;j<l;j+=i){
			w=1;
			for(int k=j;k<j+i/2;k++,w=w*wn%mod){
				ll t=c[k],u=w*c[k+i/2]%mod;
				c[k]=(t+u)%mod;
				c[k+i/2]=(t-u+mod)%mod;
			}
		}
	}
	if(fl==-1){
		ll ny=mi(l,mod-2,mod);
		for(int i=0;i<l;i++) c[i]=c[i]*ny%mod;
	}
}
void mtt(ll *c,ll mod){
	for(int i=0;i<len;i++){
		x[i]=a[i];y[i]=b[i];
	}
	ntt(x,len,1,mod);
	ntt(y,len,1,mod);
	for(int i=0;i<len;i++){
		c[i]=x[i]*y[i]%mod;
	}
	ntt(c,len,-1,mod);
}
int main()
{
	scanf("%d%d%d",&n,&m,&p);
	for(int i=0;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=0;i<=m;i++) scanf("%lld",&b[i]);
	while(len<n+m+1) len<<=1;
	mtt(c1,mod1);
	mtt(c2,mod2);
	mtt(c3,mod3);
	ll mod12=mod1*mod2,ny1=mi(mod1,mod2-2,mod2),ny12=mi(mod12%mod3,mod3-2,mod3);
	for(int i=0;i<len;i++){
		c1[i]=(((c2[i]-c1[i])%mod2+mod2)%mod2*ny1%mod2*mod1+c1[i])%mod12;
		c3[i]=(((c3[i]-c1[i])%mod3+mod3)%mod3*ny12%mod3*(mod12%p)%p+c1[i]%p)%p;
	}
	for(int i=0;i<n+m+1;i++){
		printf("%lld ",c3[i]);
	}
	return 0;
}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值