zoukankan      html  css  js  c++  java
  • [hdu1402]A * B Problem Plus(NTT)

    解题关键:快速数论变换NTT模板。

    注意$ans$数组的$ans[n]$一定要注意置$0$,或者结果从$n-1$开始遍历,这里很容易出错。

    代码1:ACdreamer 的板子。

    为什么要reverse序列至今没证明出来。=,=有懂的聚聚可以告诉本渣一下,万分感谢!!~~

    经过聚聚们的指导,还是不太懂,最终从wiki上找到了比较易懂的证明~

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<iostream>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int N=50010;
    ll G=3,P=469762049,a[3*N],b[3*N],wn[25];//P是费马素数
    char s[N],t[N];
    ll mod_pow(ll x,ll n,ll p) {
        ll res=1;x%=p;
        while(n){
            if(n&1) res=res*x%p;
            x=x*x%p;
            n>>=1;
        }
        return res;
    }
    void getwn(){ for(int i=0;i<25;i++) wn[i]=mod_pow(G,(P-1)/(1<<i),P);}//预处理w
    void NTT(ll x[],int n,int rev){
        ll w,u,v;
        for(int i=0,j=0;i!=n;i++){//构造逆序表
            if(i>j) swap(x[i],x[j]);
            for(int l=n>>1;(j^=l)<l;l>>=1);
        }
        for(int i=2,ds=1;i<n;i<<=1,ds++)
            for(int j=0;j<n;j+=i){
                w=1;
                for(int k=j;k<j+i/2;k++,w=w*wn[ds]%P){//蝴蝶操作
                    u=x[k]%P;v=w*x[k+i/2]%P;
                    x[k]=(u+v)%P;
                    x[k+i/2]=(u-v+P)%P;
                }
            }
        if(rev==-1){
            for(int i=1;i<n/2;i++) swap(x[i],x[n-i]);//把开始的wn求逆元合并到这里来了,通过什么性质至今没搞懂。
            w=mod_pow(n,P-2,P);//乘n的逆元
            for(int i=0;i<n;i++) x[i]=x[i]*w%P;
        }
    }
    void solve(){
        int i,n=1,les=(int)strlen(s),let=(int)strlen(t);
        while(n<les+let) n<<=1;
        for(i=0;i<les;i++) a[i]=s[les-i-1]-'0';
        for(i=les;i<=n;i++) a[i]=0;
        for(i=0;i<let;i++) b[i]=t[let-i-1]-'0';
        for(i=let;i<=n;i++) b[i]=0;
        NTT(a,n,1);NTT(b,n,1);
        for (i=0;i<n;i++) a[i]=a[i]*b[i]%P;
        NTT(a,n,-1);
        for(i=0;i<n;i++)
            if(a[i]>=10){//if可以不带
                a[i+1]+=a[i]/10;
                a[i]%=10;
            }
        while(n>0&&!a[n]) n--;
        for(;n>=0;n--) printf("%lld",a[n]);
        printf("
    ");
    }
    int main(){
        getwn();
        while(scanf("%s%s",s,t)!=EOF) solve();
        return 0;
    }

    代码2:

    完全按照fft转化而来

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<iostream>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int N=50010;
    ll G=3,P=469762049,a[3*N],b[3*N],wn[25],iwn[25];//P是费马素数
    char s[N],t[N];
    ll mod_pow(ll x,ll n,ll p) {
        ll res=1;x%=p;
        while(n){
            if(n&1) res=res*x%p;
            x=x*x%p;
            n>>=1;
        }
        return res;
    }
    void getwn(){
        for(int i=0;i<25;i++) wn[i]=mod_pow(G,(P-1)/(1<<i),P);
        for(int i=0;i<25;i++) iwn[i]=mod_pow(wn[i],P-2,P);
    }//预处理w
    void NTT(ll x[],int n,int rev){
        ll w,u,v,wm;
        for(int i=0,j=0;i!=n;i++){//构造逆序表
            if(i>j) swap(x[i],x[j]);
            for(int l=n>>1;(j^=l)<l;l>>=1);
        }
        for(int i=2,ds=1;i<n;i<<=1,ds++)
            for(int j=0;j<n;j+=i){
                w=1;
                if(rev==-1) wm=iwn[ds];
                else wm=wn[ds];
                for(int k=j;k<j+i/2;k++,w=w*wm%P){//蝴蝶操作
                    u=x[k]%P;v=w*x[k+i/2]%P;
                    x[k]=(u+v)%P;
                    x[k+i/2]=(u-v+P)%P;
                }
            }
        if(rev==-1){
            w=mod_pow(n,P-2,P);//乘n的逆元
            for(int i=0;i<n;i++) x[i]=x[i]*w%P;
        }
    }
    void solve(){
        int i,n=1,les=(int)strlen(s),let=(int)strlen(t);
        while(n<les+let) n<<=1;
        for(i=0;i<les;i++) a[i]=s[les-i-1]-'0';
        for(i=les;i<=n;i++) a[i]=0;
        for(i=0;i<let;i++) b[i]=t[let-i-1]-'0';
        for(i=let;i<=n;i++) b[i]=0;
        NTT(a,n,1);NTT(b,n,1);
        for (i=0;i<n;i++) a[i]=a[i]*b[i]%P;
        NTT(a,n,-1);
        for(i=0;i<n;i++)
            if(a[i]>=10){//if可以不带
                a[i+1]+=a[i]/10;
                a[i]%=10;
            }
        while(n>0&&!a[n]) n--;
        for(;n>=0;n--) printf("%lld",a[n]);
        printf("
    ");
    }
    int main(){
        getwn();
        while(scanf("%s%s",s,t)!=EOF) solve();
        return 0;
    }

    FFT常用素数P=r*2^k+1,g为P的原根
    P r k g
    3 1 1 2
    5 1 2 2
    17 1 4 3
    97 3 5 5
    193 3 6 5
    257 1 8 3
    7681 15 9 17
    12289 3 12 11
    40961 5 13 3
    65537 1 16 3
    786433 3 18 10
    5767169 11 19 3
    7340033 7 20 3
    23068673 11 21 3
    104857601 25 22 3
    167772161 5 25 3
    469762049 7 26 3
    1004535809 479 21 3
    2013265921 15 27 31
    2281701377 17 27 3
    3221225473 3 30 5
    75161927681 35 31 3
    77309411329 9 33 7

  • 相关阅读:
    yun rpm
    Codeforces Round #375 (Div. 2) D. Lakes in Berland (DFS或并查集)
    51nod 1276 1276 岛屿的数量 (很好玩的题目
    玄学C语言之scanf,printf
    51nod 算法马拉松17 解题报告 以后不能赛中写题解(查逐梦者抄袭本人代码...
    51Nod 1007 正整数分组 -简单DP
    算法马拉松13 A-E解题报告
    十五天集训_
    贴一发STL源码
    省赛反思以及未来提高计划
  • 原文地址:https://www.cnblogs.com/elpsycongroo/p/7868738.html
Copyright © 2011-2022 走看看