\(2.1\) \(\text{FFT}\)&&预处理单位复数根
别用 \(\text{STL}\) 自带复数......手写一个也不要多久。直接上模板。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <bitset>
#define ri register
#define inf 0x7fffffff
#define E (1)
#define mk make_pair
//#define int long long
//#define double long double
using namespace std; const int N=4000010; const double pi=acos(-1.0);
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch-'0'), ch=getchar();
return s*w;
}
void print(int x) { if(x<0) x=-x, putchar('-'); if(x>9) print(x/10); putchar(x%10+'0'); }
int n,m,T,rev[N];
inline void Get_Rev() { for(ri int i=0;i<T;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(T>>1):0); }
struct Complex
{
double x,y;
inline Complex (double pp=0, double qq=0) { x=pp, y=qq; }
}a[N],b[N];
inline Complex operator + (Complex a,Complex b) { return Complex(a.x+b.x,a.y+b.y); }
inline Complex operator - (Complex a,Complex b) { return Complex(a.x-b.x,a.y-b.y); }
inline Complex operator * (Complex a,Complex b) { return Complex(a.x*b.x-a.y*b.y,a.x*b.y+b.x*a.y); }
inline void FFT(Complex *s,int type)
{
for(ri int i=0;i<N;i++) if(i<rev[i]) swap(s[i],s[rev[i]]);
for(ri int mid=1;mid<T;mid<<=1)
{
Complex wn(cos(pi/mid),type*sin(pi/mid));
for(ri int r=mid<<1, j=0;j<T;j+=r)
{
Complex w(1,0);
for(ri int k=0;k<mid;k++, w=w*wn)
{
Complex x=s[j+k], y=w*s[j+mid+k];
s[j+k]=x+y, s[j+mid+k]=x-y;
}
}
}
}
signed main()
{
n=read(), m=read();
for(ri int i=0;i<=n;i++) a[i].x=read();
for(ri int i=0;i<=m;i++) b[i].x=read();
T=1; while(T<=(n+m)) T<<=1;
Get_Rev();
FFT(a,1), FFT(b,1);
for(ri int i=0;i<T;i++) a[i]=a[i]*b[i];
FFT(a,-1);
for(ri int i=0;i<=n+m;i++) printf("%d ",(int)(a[i].x/T+0.5));
puts("");
return 0;
}
系数比较大的时候精度会不够用......考虑预处理单位复数根,然后 \(\text{FFT}\) 时如果为 \(\text{IDFT}\) 显然要把虚部取反。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <bitset>
#define ri register
#define inf 0x7fffffff
#define E (1)
#define mk make_pair
//#define int long long
//#define double long double
using namespace std; const int N=4000010; const double pi=acos(-1.0);
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch-'0'), ch=getchar();
return s*w;
}
void print(int x) { if(x<0) x=-x, putchar('-'); if(x>9) print(x/10); putchar(x%10+'0'); }
int n,m,rev[N],T;
struct Complex
{
double x,y;
inline Complex (double pp=0, double qq=0) { x=pp, y=qq; }
}a[N],b[N],w[N];
inline void Get_Rev() { for(ri int i=0;i<T;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(T>>1):0); }
inline Complex operator + (Complex a,Complex b) { return Complex(a.x+b.x,a.y+b.y); };
inline Complex operator - (Complex a,Complex b) { return Complex(a.x-b.x,a.y-b.y); }
inline Complex operator * (Complex a,Complex b) { return Complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x); }
inline void FFT(Complex *s,int type)
{
for(ri int i=0;i<T;i++) if(i<rev[i]) swap(s[i],s[rev[i]]);
for(ri int mid=1;mid<T;mid<<=1)
{
for(ri int j=0, r=mid<<1;j<T;j+=r)
{
for(ri int k=0;k<mid;k++)
{
Complex qwq=w[T/mid*k];
if(type==-1) qwq.y=-qwq.y;
Complex x=s[j+k], y=qwq*s[j+mid+k];
s[j+k]=x+y, s[j+mid+k]=x-y;
}
}
}
}
signed main()
{
n=read(), m=read();
for(ri int i=0;i<=n;i++) a[i].x=read();
for(ri int i=0;i<=m;i++) b[i].x=read();
T=1; while(T<=(n+m)) T<<=1;
Get_Rev();
for(ri int i=0;i<T;i++) w[i]=Complex(cos(pi/T*i),sin(pi/T*i));
FFT(a,1), FFT(b,1);
for(ri int i=0;i<T;i++) a[i]=a[i]*b[i];
FFT(a,-1);
for(ri int i=0;i<=n+m;i++) printf("%d ",(int)(a[i].x/T+0.5));
puts("");
return 0;
}
\(2.2\) \(\text{NTT}\)&&预处理原根幂
直接上模板。只适用于模数为 \(\text{NTT}\) 模数的情况。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <bitset>
#define ri register
#define inf 0x7fffffff
#define E (1)
#define mk make_pair
//#define int long long
//#define double long double
using namespace std; const int N=4000010, Mod=998244353;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch-'0'), ch=getchar();
return s*w;
}
void print(int x) { if(x<0) x=-x, putchar('-'); if(x>9) print(x/10); putchar(x%10+'0'); }
int n,m,a[N],b[N],T=1,rev[N]; char s[N];
inline void Get_Rev() { for(ri int i=0;i<T;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(T>>1):0); }
inline int ksc(int x,int p) { int res=1; for(;p;p>>=1, x=1ll*x*x%Mod) if(p&1) res=1ll*res*x%Mod; return res; }
inline void NTT(int *s,int type)
{
for(ri int i=0;i<T;i++) if(i<rev[i]) swap(s[i],s[rev[i]]);
for(ri int i=2,cnt=1;i<=T;cnt++, i<<=1)
{
int wn=ksc(3,(Mod-1)/i);
if(!type) wn=ksc(wn,Mod-2);
for(ri int j=0, mid=(i>>1);j<T;j+=i)
{
for(ri int k=0, w=1;k<mid;k++, w=1ll*w*wn%Mod)
{
int x=s[j+k], y=1ll*w*s[j+mid+k]%Mod;
s[j+k]=(x+y)%Mod, s[j+mid+k]=(x-y+Mod)%Mod;
}
}
}
if(!type) for(ri int i=0,inv=ksc(T,Mod-2);i<T;i++) s[i]=1ll*s[i]*inv%Mod;
}
signed main()
{
n=read(), m=read();
for(ri int i=0;i<=n;i++) a[i]=read();
for(ri int i=0;i<=m;i++) b[i]=read();
T=1; while(T<=(n+m)) T<<=1;
Get_Rev();
NTT(a,1), NTT(b,1);
for(ri int i=0;i<T;i++) a[i]=1ll*a[i]*b[i]%Mod;
NTT(a,0);
for(ri int i=0;i<=n+m;i++) printf("%d ",a[i]);
puts("");
return 0;
}
考虑多次 \(\text{NTT}\) 时可以预处理原根幂。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <bitset>
#define ri register
#define inf 0x7fffffff
#define E (1)
#define mk make_pair
//#define int long long
//#define double long double
using namespace std; const int N=4000010, Mod=998244353;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch-'0'), ch=getchar();
return s*w;
}
void print(int x) { if(x<0) x=-x, putchar('-'); if(x>9) print(x/10); putchar(x%10+'0'); }
int n,m,a[N],b[N],T=1,rev[N],f[25][2]; char s[N];
inline void Get_Rev() { for(ri int i=0;i<T;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(T>>1):0); }
inline int ksc(int x,int p) { int res=1; for(;p;p>>=1, x=1ll*x*x%Mod) if(p&1) res=1ll*res*x%Mod; return res; }
inline void NTT(int *s,int type)
{
for(ri int i=0;i<T;i++) if(i<rev[i]) swap(s[i],s[rev[i]]);
for(ri int i=2,cnt=1;i<=T;cnt++, i<<=1)
{
int wn=f[cnt][type];
for(ri int j=0, mid=(i>>1);j<T;j+=i)
{
for(ri int k=0, w=1;k<mid;k++, w=1ll*w*wn%Mod)
{
int x=s[j+k], y=1ll*w*s[j+mid+k]%Mod;
s[j+k]=(x+y)%Mod, s[j+mid+k]=(x-y+Mod)%Mod;
}
}
}
if(!type) for(ri int i=0,inv=ksc(T,Mod-2);i<T;i++) s[i]=1ll*s[i]*inv%Mod;
}
signed main()
{
f[23][1]=ksc(3,119), f[23][0]=ksc(332748118,119);
for(ri int i=22;~i;i--) f[i][1]=1ll*f[i+1][1]*f[i+1][1]%Mod, f[i][0]=1ll*f[i+1][0]*f[i+1][0]%Mod;
n=read(), m=read();
for(ri int i=0;i<=n;i++) a[i]=read();
for(ri int i=0;i<=m;i++) b[i]=read();
T=1; while(T<=(n+m)) T<<=1;
Get_Rev();
NTT(a,1), NTT(b,1);
for(ri int i=0;i<T;i++) a[i]=1ll*a[i]*b[i]%Mod;
NTT(a,0);
for(ri int i=0;i<=n+m;i++) printf("%d ",a[i]);
puts("");
return 0;
}
\(2.3\) \(\text{MTT}\)&&拆系数\(\text{FFT}\)
考虑到数据范围较大,例如,当两个长度为 \(10^{5}\) 级别的序列卷积且模数为 \(10^{9}\) 级别(不为 \(\text{NTT}\) 模数)。肯定不能直接 \(\text{NTT}\) 了。直接 \(\text{FFT}\) 的话,每个数的结果大约为 \(10^{5}\times 10^{9} \times 10^{9}=10^{23}\),超出了 \(2^{64}\),浮点数会出现较大的误差。故可以考虑使用拆系数 \(\text{FFT}\) 和三模数 \(\text{NTT}\)。\(2.3\) 中将简略讲明拆系数 \(\text{FFT}\)。
设 \(P\) 为模数大小。把 \(A(x)\) 和 \(B(x)\) 的每一项分别拆成 \(aQ+b\)。记 \(A(x)=A1(x)Q+A2(x)\),\(B(x)=B1(x)Q+B2(x)\)。则显然有:
\(4\) 次 \(\text{DFT}\) 和 \(4\) 次 \(\text{IDFT}\) 即可解决。考虑令 \(Q=\sqrt{x}\),则卷积结果大约在 \(10^{14}\)。发现此时需要预处理单位复数根。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <bitset>
#define ri register
#define inf 0x7fffffff
#define E (1)
#define mk make_pair
//#define int long long
//#define double long double
using namespace std; const int N=400010; const double pi=acos(-1.0);
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch-'0'), ch=getchar();
return s*w;
}
void print(int x) { if(x<0) x=-x, putchar('-'); if(x>9) print(x/10); putchar(x%10+'0'); }
int n,m,P,T,rev[N],a[N],b[N],blk,ans[N];
struct Complex
{
double x,y;
inline Complex ( double pp=0, double qq=0) { x=pp, y=qq; }
}f1[N],f2[N],g1[N],g2[N],w[N],d1[N],d2[N],d3[N],d4[N];
inline Complex operator + (Complex a,Complex b) { return Complex(a.x+b.x,a.y+b.y); }
inline Complex operator - (Complex a,Complex b) { return Complex(a.x-b.x,a.y-b.y); }
inline Complex operator * (Complex a,Complex b) { return Complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x); }
inline void Get_Rev() { for(ri int i=0;i<T;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(T>>1):0); }
inline void Init()
{
T=1;
while(T<=n+m) T<<=1;
Get_Rev();
for(ri int i=0;i<T;i++) w[i]=Complex(cos(pi/T*i),sin(pi/T*i));
}
inline void FFT(Complex *s,int type)
{
for(ri int i=0;i<T;i++) if(i<rev[i]) swap(s[i],s[rev[i]]);
for(ri int mid=1;mid<T;mid<<=1)
{
for(ri int j=0, r=mid<<1;j<T;j+=r)
{
for(ri int k=0;k<mid;k++)
{
Complex qwq=w[T/mid*k];
if(type==-1) qwq.y=-qwq.y;
Complex x=s[j+k], y=qwq*s[j+mid+k];
s[j+k]=x+y, s[j+mid+k]=x-y;
}
}
}
}
signed main()
{
n=read(), m=read(), P=read();
Init();
for(ri int i=0;i<=n;i++) a[i]=read();
for(ri int i=0;i<=m;i++) b[i]=read();
blk=32768;
for(ri int i=0;i<=n;i++) f1[i]=Complex(a[i]/blk,0), f2[i]=Complex(a[i]%blk,0);
for(ri int i=0;i<=m;i++) g1[i]=Complex(b[i]/blk,0), g2[i]=Complex(b[i]%blk,0);
FFT(f1,1), FFT(f2,1), FFT(g1,1), FFT(g2,1);
for(ri int i=0;i<T;i++) d1[i]=f1[i]*g1[i]; FFT(d1,-1);
for(ri int i=0;i<T;i++) d2[i]=f1[i]*g2[i]; FFT(d2,-1);
for(ri int i=0;i<T;i++) d3[i]=f2[i]*g1[i]; FFT(d3,-1);
for(ri int i=0;i<T;i++) d4[i]=f2[i]*g2[i]; FFT(d4,-1);
for(ri int i=0;i<T;i++)
{
ans[i]=
(1ll*(long long)(d1[i].x/T+0.5)%P*blk%P*blk%P+
1ll*(long long)(d2[i].x/T+0.5)%P*blk%P)%P;
ans[i]=(ans[i]+1ll*(long long)(d3[i].x/T+0.5)%P*blk%P)%P;
ans[i]=(ans[i]+1ll*(long long)(d4[i].x/T+0.5)%P)%P;
}
for(ri int i=0;i<=n+m;i++) printf("%d ",ans[i]);
puts("");
return 0;
}
\(2.4\) \(\text{MTT}\)&&三模数\(\text{NTT}\)
\(2.4\) 中将简略讲明三模数 \(\text{NTT}\)。
考虑找 \(3\) 个大小在 \(10^{9}\) 级别的 \(\text{NTT}\) 模数,如 \(\{469762049,998244353,1004535809\}\),它们的原根都是 \(3\),实际实现起来也非常方便,并且它们的乘积比 \(10^{23}\) 要大。
考虑分别在 \(3\) 个模数意义下求卷积结果,然后中国剩余定理合并。但是发现 \(p_{1}\times p_{2}\times p_{3}>10^{23}\),显然它们会爆 \(long\) \(long\)。这个问题可以用 __\(int128\) 或者手写高精解决。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <bitset>
#define ri register
#define inf 0x7fffffff
#define E (1)
#define mk make_pair
//#define int long long
//#define double long double
#define int __int128
using namespace std; const int N=400010, Mod1=469762049, Mod2=998244353, Mod3=1004535809;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch-'0'), ch=getchar();
return s*w;
}
void print(int x) { if(x<0) x=-x, putchar('-'); if(x>9) print(x/10); putchar(x%10+'0'); }
int n,m,P,rev[N],T,a[4][N],b[4][N],f[N];
inline void Get_Rev() { for(ri int i=0;i<T;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(T>>1):0); }
inline int ksc(int x,int p,int Mod) { int res=1; for(;p;p>>=1, x=1ll*x*x%Mod) if(p&1) res=1ll*res*x%Mod; return res; }
inline void NTT(int *s,int type,int Mod)
{
for(ri int i=0;i<T;i++) if(i<rev[i]) swap(s[i],s[rev[i]]);
for(ri int i=2;i<=T;i<<=1)
{
int wn=ksc(3,(Mod-1)/i,Mod);
if(!type) wn=ksc(wn,Mod-2,Mod);
for(ri int j=0, mid=(i>>1);j<T;j+=i)
{
for(ri int k=0, w=1;k<mid;k++, w=1ll*w*wn%Mod)
{
int x=s[j+k], y=1ll*w*s[j+mid+k]%Mod;
s[j+k]=(x+y)%Mod, s[j+mid+k]=(x-y+Mod)%Mod;
}
}
}
if(!type) for(ri int i=0, inv=ksc(T,Mod-2,Mod);i<T;i++) s[i]=1ll*s[i]*inv%Mod;
}
int exgcd(int a,int b,int &x,int &y)
{
if(!b) { x=1, y=0; return a; }
int tt=exgcd(b,a%b,y,x);
y-=a/b*x; return tt;
}
signed main()
{
n=read(), m=read(), P=read();
for(ri int i=0;i<=n;i++) a[1][i]=a[2][i]=a[3][i]=read();
for(ri int i=0;i<=m;i++) b[1][i]=b[2][i]=b[3][i]=read();
T=1; while(T<=n+m) T<<=1; Get_Rev();
NTT(a[1],1,Mod1), NTT(b[1],1,Mod1);
for(ri int i=0;i<T;i++) a[1][i]=1ll*a[1][i]*b[1][i]%Mod1;
NTT(a[1],0,Mod1);
NTT(a[2],1,Mod2), NTT(b[2],1,Mod2);
for(ri int i=0;i<T;i++) a[2][i]=1ll*a[2][i]*b[2][i]%Mod2;
NTT(a[2],0,Mod2);
NTT(a[3],1,Mod3), NTT(b[3],1,Mod3);
for(ri int i=0;i<T;i++) a[3][i]=1ll*a[3][i]*b[3][i]%Mod3;
NTT(a[3],0,Mod3);
//for(ri int i=0;i<=n+m;i++) printf("%d %d %d\n",a[1][i],a[2][i],a[3][i]);
for(ri int i=0;i<T;i++)
{
int gt=Mod1, res=a[1][i];
for(ri int j=2;j<=3;j++)
{
int md=(j==2)?Mod2:Mod3;
int dd=(a[j][i]-res%md+md)%md;
int x,y; x=y=0;
int c=exgcd(gt,md,x,y);
x=1ll*x*(dd/c)%(md/c);
res=1ll*(res+1ll*x*gt);
gt=1ll*gt*md/c;
res=(res%gt+gt)%gt;
}
f[i]=(res%gt+gt)%gt;
f[i]%=P;
}
for(ri int i=0;i<=n+m;i++) print(f[i]), putchar(' ');
puts("");
return 0;
}
但是,__\(int128\) 的常数非常大,而且在实际比赛时也一般无法使用。考虑有什么更高明的做法。
考虑先只合并前两个模数。设最终答案为 \(s\),且有
前两项显然可以在 \(long\) \(long\) 范围内合并,记
存在 \(s=s_{4}+w_{1}p_{1}p_{2}=s_{3}+w_{2}p_{3}\)
显然有 \(w_{1}<p_{3}\),则有
那么可以求出 \(w_{1}\) 的真实值,那么通过上式,即可在模 \(P\) 意义下求出 \(s\) 的值。但是常数非常大。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <bitset>
#define ri register
#define inf 0x7fffffff
#define E (1)
#define mk make_pair
//#define int long long
//#define double long double
using namespace std; const int N=400010, Mod1=469762049, Mod2=998244353, Mod3=1004535809;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch-'0'), ch=getchar();
return s*w;
}
void print(int x) { if(x<0) x=-x, putchar('-'); if(x>9) print(x/10); putchar(x%10+'0'); }
int n,m,P,rev[N],T,a[4][N],b[4][N];
long long f[N];
inline void Get_Rev() { for(ri int i=0;i<T;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(T>>1):0); }
inline int ksc(int x,int p,int Mod) { int res=1; for(;p;p>>=1, x=1ll*x*x%Mod) if(p&1) res=1ll*res*x%Mod; return res; }
inline void NTT(int *s,int type,int Mod)
{
for(ri int i=0;i<T;i++) if(i<rev[i]) swap(s[i],s[rev[i]]);
for(ri int i=2;i<=T;i<<=1)
{
int wn=ksc(3,(Mod-1)/i,Mod);
if(!type) wn=ksc(wn,Mod-2,Mod);
for(ri int j=0, mid=(i>>1);j<T;j+=i)
{
for(ri int k=0, w=1;k<mid;k++, w=1ll*w*wn%Mod)
{
int x=s[j+k], y=1ll*w*s[j+mid+k]%Mod;
s[j+k]=(x+y)%Mod, s[j+mid+k]=(x-y+Mod)%Mod;
}
}
}
if(!type) for(ri int i=0, inv=ksc(T,Mod-2,Mod);i<T;i++) s[i]=1ll*s[i]*inv%Mod;
}
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if(!b) { x=1, y=0; return a; }
int tt=exgcd(b,a%b,y,x);
y-=a/b*x; return tt;
}
signed main()
{
n=read(), m=read(), P=read();
for(ri int i=0;i<=n;i++) a[1][i]=a[2][i]=a[3][i]=read();
for(ri int i=0;i<=m;i++) b[1][i]=b[2][i]=b[3][i]=read();
T=1; while(T<=n+m) T<<=1; Get_Rev();
NTT(a[1],1,Mod1), NTT(b[1],1,Mod1);
for(ri int i=0;i<T;i++) a[1][i]=1ll*a[1][i]*b[1][i]%Mod1;
NTT(a[1],0,Mod1);
NTT(a[2],1,Mod2), NTT(b[2],1,Mod2);
for(ri int i=0;i<T;i++) a[2][i]=1ll*a[2][i]*b[2][i]%Mod2;
NTT(a[2],0,Mod2);
NTT(a[3],1,Mod3), NTT(b[3],1,Mod3);
for(ri int i=0;i<T;i++) a[3][i]=1ll*a[3][i]*b[3][i]%Mod3;
NTT(a[3],0,Mod3);
//for(ri int i=0;i<=n+m;i++) printf("%d %d %d\n",a[1][i],a[2][i],a[3][i]);
for(ri int i=0;i<T;i++)
{
long long gt=Mod1; long long res=a[1][i];
long long md=Mod2, qwq=1ll*Mod1*Mod2;
long long dd=(a[2][i]-res%md+md)%md;
long long x,y; x=y=0;
long long c=exgcd(gt,md,x,y);
x=x*(dd/c)%(md/c);
res=(res+x*gt);
gt=gt*(md/c);
res=(res%gt+gt)%gt;
long long yy=a[3][i];
long long k=((yy-res)%Mod3+Mod3)%Mod3*ksc(qwq%Mod3,Mod3-2,Mod3)%Mod3;
f[i]=(res+k%P*Mod1%P*Mod2%P)%P;
}
for(ri int i=0;i<=n+m;i++) print(f[i]), putchar(' ');
puts("");
return 0;
}
来源:oschina
链接:https://my.oschina.net/u/4295464/blog/4479828