zoukankan      html  css  js  c++  java
  • 洛谷 P4245 [模板]任意模数NTT —— 三模数NTT / 拆系数FFT(MTT)

    题目:https://www.luogu.org/problemnew/show/P4245

    用三模数NTT做,需要注意时间和细节;

    注意各种地方要取模!传入 upt() 里面的数一定要不超过2倍 mod!

    乘法会爆 long long 时用快速乘!

    两次合并的模数,第一次是 (ll) p1*p2,第二次直接对题目的模数取模即可!

    注意局部开 (ll)!

    合并时用到的逆元每次都一样,所以要先处理好而不是现场快速幂算!!

    然而为什么时间还是 Narh 的两倍!

    一晚上的心血...

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int const xn=(1<<19);
    int n,m,lim,rev[xn],a[5][xn],b[5][xn],p[5]={0,469762049,998244353,1004535809};
    ll mod;
    int rd()
    {
      int ret=0,f=1; char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return f?ret:-ret;
    }
    ll upt(ll x,int md){while(x>=md)x-=md; while(x<0)x+=md; return x;}
    ll mul(ll a,ll b,int md)
    {
      ll ret=0; a=a%md; b=b%md;
      if(a<0)a+=md; if(b<0)b+=md;//
      for(;b;b>>=1ll,a=(a+a)%md)if(b&1)ret=(ret+a)%md;
      return ret;
    }
    ll pw(ll a,int b,int md)
    {
      ll ret=1; a=a%md;
      for(;b;b>>=1,a=mul(a,a,md))if(b&1)ret=mul(ret,a,md);//mul!!
      return ret;
    }
    void ntt(int *a,int tp,int md)
    {
      for(int i=0;i<lim;i++)
        if(i<rev[i])swap(a[i],a[rev[i]]);
      for(int mid=1;mid<lim;mid<<=1)
        {
          int wn=pw(3,(md-1)/(mid<<1),md);
          if(tp==-1)wn=pw(wn,md-2,md);
          for(int j=0,len=(mid<<1);j<lim;j+=len)
        {
          int w=1;
          for(int k=0;k<mid;k++,w=(ll)w*wn%md)
            {
              int x=a[j+k],y=(ll)w*a[j+mid+k]%md;
              a[j+k]=upt(x+y,md); a[j+mid+k]=upt(x-y,md);
            }
        }
        }
      if(tp==1)return; int inv=pw(lim,md-2,md);
      for(int i=0;i<lim;i++)a[i]=(ll)a[i]*inv%md;
    }
    ll uni(ll r1,ll r2,ll m1,int m2,int tp,int inv)
    {
      ll k=mul(r2-r1,inv,m2);
      if(!tp)return (r1+k*m1)%(m1*m2);
      return upt((r1+mul(k,m1,mod))%mod,mod);//%mod!!
    }
    int main()
    {
      n=rd(); m=rd(); mod=rd();
      for(int i=0;i<=n;i++)a[1][i]=a[2][i]=a[3][i]=rd();
      for(int i=0;i<=m;i++)b[1][i]=b[2][i]=b[3][i]=rd();
      lim=1; int l=0;
      while(lim<=n+m+2)lim<<=1,l++;//+2!
      for(int i=0;i<lim;i++)
        rev[i]=((rev[i>>1]>>1)|((i&1)<<(l-1)));
      for(int i=1;i<=3;i++)
        {
          ntt(a[i],1,p[i]); ntt(b[i],1,p[i]);
          for(int j=0;j<lim;j++)a[i][j]=(ll)a[i][j]*b[i][j]%p[i];
          ntt(a[i],-1,p[i]);
        }
      int inv1=pw(p[1],p[2]-2,p[2]);
      int inv2=pw((ll)p[1]*p[2],p[3]-2,p[3]);//inv!!
      for(int i=0;i<=n+m;i++)
        {
          ll ans=uni(a[1][i],a[2][i],p[1],p[2],0,inv1);
          ans=uni(ans,a[3][i],(ll)p[1]*p[2],p[3],1,inv2);//(ll)!!!
          printf("%lld ",ans);
        }
      puts("");
      return 0;
    }

    关于拆系数FFT,这篇博客说得十分清晰:https://blog.csdn.net/lvzelong2014/article/details/80156989

    而且代码也十分简洁优美,所以就模仿着写了;

    注意:

    1. 读入的初始数组要先取模;

    2. 对 (x<<30) 开 (ll) 要写在括号里面而非外面;

    3. IDFT中最后要 /lim,平常都之写 a[i].x/lim,但这里因为用到了 y,所以必须加上 a[i].y/lim!!

    4. 卡精度,要开 long double

    (注意数组范围,如果要用到 a[lim] 的话,再稍微把数组开大一点)

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    typedef long double db;//
    int const xn=(1<<18)+5;//a[lim]
    db const Pi=acos(-1.0);
    int n,m,lim,rev[xn],P,f[xn],g[xn],ans[xn];
    struct com{db x,y;}a[xn],b[xn],Da[xn],Db[xn],Dc[xn],Dd[xn];
    com operator + (com a,com b){return (com){a.x+b.x,a.y+b.y};}
    com operator - (com a,com b){return (com){a.x-b.x,a.y-b.y};}
    com operator * (com a,com b){return (com){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};}
    com conj(com a){return (com){a.x,-a.y};}
    int rd()
    {
      int ret=0,f=1; char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return f?ret:-ret;
    }
    void init()
    {
      lim=1; int l=0;
      while(lim<=n+m)lim<<=1,l++;
      for(int i=0;i<lim;i++)rev[i]=((rev[i>>1]>>1)|((i&1)<<(l-1)));
    }
    int upt(int x){while(x>=P)x-=P; while(x<0)x+=P; return x;}
    void fft(com *a,int tp)
    {
      for(int i=0;i<lim;i++)
        if(i<rev[i])swap(a[i],a[rev[i]]);
      for(int mid=1;mid<lim;mid<<=1)
        {
          com wn=(com){cos(Pi/mid),tp*sin(Pi/mid)};
          for(int j=0,len=(mid<<1);j<lim;j+=len)
        {
          com w=(com){1,0};
          for(int k=0;k<mid;k++,w=w*wn)
            {
              com x=a[j+k],y=w*a[j+mid+k];
              a[j+k]=x+y; a[j+mid+k]=x-y;
            }
        }
        }
      if(tp==1)return;
      for(int i=0;i<lim;i++)a[i].x/=lim,a[i].y/=lim;//y!! for use y
    }
    void mul(int *A,int *B,int *C)
    {
      for(int i=0;i<lim;i++)A[i]=upt(A[i]%P),B[i]=upt(B[i]%P);//
      int M=(1<<15)-1;
      for(int i=0;i<lim;i++)a[i]=(com){A[i]&M,A[i]>>15};
      for(int i=0;i<lim;i++)b[i]=(com){B[i]&M,B[i]>>15};
      fft(a,1); fft(b,1);
      a[lim]=a[0]; b[lim]=b[0];//
      for(int i=0,j=lim;i<lim;i++,j--)
        {
          com da,db,dc,dd;
          da=(a[i]+conj(a[j]))*(com){0.5,0};
          db=(a[i]-conj(a[j]))*(com){0,-0.5};
          dc=(b[i]+conj(b[j]))*(com){0.5,0};
          dd=(b[i]-conj(b[j]))*(com){0,-0.5};
          Da[i]=da*dc; Db[i]=da*dd; Dc[i]=db*dc; Dd[i]=db*dd;
        }
      a[lim]=b[lim]=(com){0,0};
      for(int i=0;i<lim;i++)a[i]=Da[i]+Db[i]*(com){0,1};
      for(int i=0;i<lim;i++)b[i]=Dc[i]+Dd[i]*(com){0,1};
      fft(a,-1); fft(b,-1);
      for(int i=0;i<=n+m;i++)
        {
          int da=(ll)(a[i].x+0.5)%P;
          int db=(ll)(a[i].y+0.5)%P;
          int dc=(ll)(b[i].x+0.5)%P;
          int dd=(ll)(b[i].y+0.5)%P;
          C[i]=(da+((ll)(db+dc)<<15)+((ll)dd<<30))%P;//(ll)
        }
    }
    int main()
    {
      n=rd(); m=rd(); P=rd(); init();
      for(int i=0;i<=n;i++)f[i]=rd();
      for(int i=0;i<=m;i++)g[i]=rd();
      mul(f,g,ans);
      for(int i=0;i<=n+m;i++)printf("%d ",upt(ans[i])); puts("");
      return 0;
    }
  • 相关阅读:
    POJ3320 Jessica's Reading Problem
    POJ3320 Jessica's Reading Problem
    CodeForces 813B The Golden Age
    CodeForces 813B The Golden Age
    An impassioned circulation of affection CodeForces
    An impassioned circulation of affection CodeForces
    Codeforces Round #444 (Div. 2) B. Cubes for Masha
    2013=7=21 进制转换
    2013=7=15
    2013=7=14
  • 原文地址:https://www.cnblogs.com/Zinn/p/10035728.html
Copyright © 2011-2022 走看看