多项式模板

旧时模样 提交于 2020-10-25 00:55:45

\(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)\)。则显然有:

\[\qquad A(x)*B(x)=A1(x)B1(x)Q^{2}+A1(x)B2(x)Q+A2(x)B1(x)Q+A2(x)B2(x) \qquad \]

\(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\),且有

\[\qquad s\equiv s_{1}\pmod {p_{1}} \qquad \]

\[\qquad s\equiv s_{2}\pmod {p_{2}} \qquad \]

\[\qquad s\equiv s_{3}\pmod {p_{3}} \qquad \]

前两项显然可以在 \(long\) \(long\) 范围内合并,记

\[\qquad s\equiv s_{4}\pmod {p_{1}p_{2}} \qquad \]

存在 \(s=s_{4}+w_{1}p_{1}p_{2}=s_{3}+w_{2}p_{3}\)

显然有 \(w_{1}<p_{3}\),则有

\[\qquad w_{1}\equiv (s_{3}-s_{4})(p_{1}p_{2})^{-1}\pmod {p_{3}} \qquad \]

那么可以求出 \(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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!