[模板]任意模数NTT

一、题目

点此看题

二、解法

所谓任意模数 NTT \text{NTT} 其实就是跑三遍普通 NTT \text{NTT} ,用大质数来还原结果。
我们选的质数是: 469762049 , 998244353 , 1004535809 469762049,998244353,1004535809 ,它们的原根都是 3 3
设我们跑出来的三个答案分别是 a n s 1 , a n s 2 , a n s 3 ans_1,ans_2,ans_3 ,我们先用中国剩余定理求出 x = a n s 4 m o d    p 1 p 2 x=ans_4\mod p_1p_2 ,所以 a n s 4 + k p 1 p 2 = x ans_4+kp_1p_2=x ,我们现在的问题就在于求 k k ,用 a n s 3 ans_3 ,可得: k p 1 p 2 = a n s 3 a n s 4 m o d    p 3 kp_1p_2=ans_3-ans_4\mod p_3 ,所以 k = ( a n s 3 a n s 4 ) p 1 1 p 2 1 m o d    p 3 k=(ans_3-ans_4)p_1^{-1}p_2^{-1}\mod p_3 ,求出 k k 后,算出初始的 x x ,然后就可以模 p p 算出答案。
这也作证了为什么我们要用超大质数,我这道题写了 __int128 \text{\_\_int128}

#include <cstdio>
#include <iostream>
#include <cmath>
#define int __int128
using namespace std;
const int MAXN = 300005;
int read()
{
    int num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
    return num*flag;
}
int n,m,p,lg,a[MAXN],b[MAXN],Rev[MAXN],ans[MAXN][4];
int p1=469762049,p2=998244353,p3=1004535809;
int A[MAXN]={},B[MAXN]={},len=1;
int qkpow(int a,int b,int mod)
{
	int res=1;
	while(b>0)
	{
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}
int exgcd(int a,int b,int &x,int &y)
{
	if(b==0){x=1;y=0;return a;}
	int d=exgcd(b,a%b,y,x);
	y-=(a/b)*x;
	return d;
}
void NTT(int *a,const int len,int tmp,int MOD)
{
	
	for(int i=0;i<len;i++)
	{
		Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(lg-1));
		if(i<Rev[i])
			swap(a[i],a[Rev[i]]);
	}
	for(int s=2;s<=len;s<<=1)
	{
		int t=s/2,w=(tmp==1)?qkpow(3,(MOD-1)/s,MOD):qkpow(3,(MOD-1)-(MOD-1)/s,MOD);
		for(int i=0;i<len;i+=s)
		{
			int x=1;
			for(int j=0;j<t;j++,x=x*w%MOD)
			{
				int fe=a[i+j],fo=a[i+j+t];
				a[i+j]=(fe+x*fo)%MOD;
				a[i+j+t]=((fe-fo*x)%MOD+MOD)%MOD;
			}
		}
	}
	if(tmp==1) return ;
	int inv=qkpow(len,MOD-2,MOD);
	for(int i=0;i<len;i++)
		a[i]=a[i]*inv%MOD;
}
void fuck(int p,int id)
{
	len=lg=1;while(len<=n+m+2) len<<=1,lg++;
	lg--;
	for(int i=0;i<len;i++) A[i]=B[i]=0;
	for(int i=0;i<=n;i++) A[i]=a[i];
	for(int i=0;i<=m;i++) B[i]=b[i];
	NTT(A,len,1,p);NTT(B,len,1,p);
	for(int i=0;i<len;i++) A[i]=A[i]*B[i]%p;
	NTT(A,len,-1,p);
	for(int i=0;i<=n+m;i++)
		ans[i][id]=A[i];
}
int excrt(int a,int b)
{
	int m[3]={0,p1,p2},r[3]={0,a,b};
	int M=m[1],R=r[1],x,y;
	for(int i=2;i<=2;i++)
	{
		int d=exgcd(M,m[i],x,y);
		if((R-r[i])%d) return -1;
		x=x*(R-r[i])/d%m[i];
		R-=x*M;
		M=M*m[i]/d;
		R%=M;
	}
	return (R%M+M)%M;
}
signed main()
{
	n=read();m=read();p=read();
	for(int i=0;i<=n;i++) a[i]=read();
	for(int i=0;i<=m;i++) b[i]=read();
	fuck(p1,1);
	fuck(p2,2);
	fuck(p3,3);
	for(int i=0;i<=n+m;i++)
	{
		int x=excrt(ans[i][1],ans[i][2]);
		int y=(ans[i][3]-x)*qkpow(p1,p3-2,p3)%p3*qkpow(p2,p3-2,p3)%p3;
		y=(y%p3+p3)%p3;
		long long t=((y*p1%p*p2%p+x)%p+p)%p;
		printf("%lld ",t); 
	}
}
发布了193 篇原创文章 · 获赞 12 · 访问量 3374

猜你喜欢

转载自blog.csdn.net/C202044zxy/article/details/103613411