• 【积累】The 2019 Asia Nanchang First Round Online Programming Contest The Nth Item (矩阵快速幂 k进制快速幂)


    The Nth Item

    q%100000,不是正解。

    题解是要生成函数+二次剩余。

    蔡队说,k进制快速幂。

    ////////////////////////////

    然后晚上终于学会了k进制快速幂。感谢蔡队。

    在后边放了代码。

     /////////////////////////////

    二进制矩阵快速幂做法 +q%100000(232ms):

    #include<bits/stdc++.h> 
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    const int maxn=1e3+50;
    const int inf=0x3f3f3f3f;
    ll jkpow(ll n)
    {
        int i,j,k,t;
        ll ans[2]={1,0};
        ll base[2][2]={3,2,1,0};
        ll tempa[2],tempb[2][2];
        while(n)
        {
            if(n&1)
            {
                memset(tempa,0,sizeof(tempa));
                for(i=0;i<2;i++)
                    for(j=0;j<2;j++)
                        tempa[i]=(tempa[i]+base[i][j]*ans[j]%mod)%mod;
                for(i=0;i<2;i++)ans[i]=tempa[i]%mod;
            }
            memset(tempb,0,sizeof(tempb));
            for(i=0;i<2;i++)
                for(j=0;j<2;j++)
                    for(k=0;k<2;k++)
                        tempb[i][j]=(tempb[i][j]+base[i][k]*base[k][j]%mod)%mod;
            for(i=0;i<2;i++)
                for(j=0;j<2;j++)
                    base[i][j]=tempb[i][j]%mod;
            n>>=1;
        }
        return ans[0]%mod;
    }
    int main()
    {
        ll q,n,a,ans=-1;
        scanf("%lld%lld",&q,&n);
        if(q>100000)q=100000;
        for(int i=1;i<=q;i++)
        {
            if(n)a=jkpow(n-1);else a=0;
            n^=(a*a);
            if(ans==-1)ans=a;
            else ans^=a;
        }
        printf("%lld
    ",ans);
    }

     带初始化优化的二进制矩阵快速幂做法+q%100000(71ms):

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    const int maxn=1e3+50;
    const int inf=0x3f3f3f3f;
    ll base[70][2][2];
    void init()
    {
        int i,j,k,u;
        base[1][0][0]=3;base[1][0][1]=2;base[1][1][0]=1;base[1][1][1]=0;
        ll tempb[2][2];
        for(u=2;u<=64;u++)
        {
            memset(tempb,0,sizeof(tempb));
            for(i=0;i<2;i++)
                for(j=0;j<2;j++)
                    for(k=0;k<2;k++)
                        tempb[i][j]=(tempb[i][j]+base[u-1][i][k]*base[u-1][k][j]%mod)%mod;
            for(i=0;i<2;i++)
                for(j=0;j<2;j++)
                    base[u][i][j]=tempb[i][j]%mod;
        }
    }
    ll jkpow(ll n)
    {
        int i,j,k,t,u=1;
        ll ans[2]={1,0};
        ll tempa[2],tempb[2][2];
        while(n)
        {
            if(n&1)
            {
                memset(tempa,0,sizeof(tempa));
                for(i=0;i<2;i++)
                    for(j=0;j<2;j++)
                        tempa[i]=(tempa[i]+base[u][i][j]*ans[j]%mod)%mod;
                for(i=0;i<2;i++)ans[i]=tempa[i]%mod;
            }
            memset(tempb,0,sizeof(tempb));
            u++;
            n>>=1;
        }
        return ans[0]%mod;
    }
    int main()
    {
        init();
        ll q,n,a,ans=-1;
        scanf("%lld%lld",&q,&n);
        if(q>100000)q=100000;
        for(int i=1;i<=q;i++)
        {
            if(n-1)a=jkpow(n-1);else a=0;
            n^=(a*a);
            if(ans==-1)ans=a;
            else ans^=a;
            if(!a)break;
        }
        printf("%lld
    ",ans);
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    k进制快速幂!虽说是995ms,但好歹是实打实跑了1e7的询问。上面的代码也只能跑1e6吧。

    大写 开心! 感谢蔡队!

    (矩阵的看不懂参照看下一份代码的整数k进制快速幂代码,附了简单的注释)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    const int maxn=1e3+50;
    const int inf=0x3f3f3f3f;
    //65535 
    ll base[5][65540][2][2];
    void init()
    {
        int i,j,k,u,t;
        base[0][1][0][0]=3;base[0][1][0][1]=2;
        base[0][1][1][0]=1;base[0][1][1][1]=0;
        ll tempb[5][2][2];
        for(t=0;t<=3;t++)
        {
            if(t)
            {
                for(i=0;i<2;i++)
                    for(j=0;j<2;j++)
                        base[t][1][i][j]=base[t-1][65536][i][j];
            }
            for(u=2;u<=65536;u++)
            {
                memset(tempb,0,sizeof(tempb));
                for(i=0;i<2;i++)
                    for(j=0;j<2;j++)
                        for(k=0;k<2;k++)
                                tempb[t][i][j]=(tempb[t][i][j]+base[t][1][i][k]*base[t][u-1][k][j]%mod)%mod;
                for(i=0;i<2;i++)
                    for(j=0;j<2;j++)
                        base[t][u][i][j]=tempb[t][i][j]%mod;
            }
        }
        
    }
    ll jkpow(ll n)
    {
        int i,j,k,t,u=0,re;
        ll ans[2]={1,0};
        ll tempa[2];
        ll tbase[2][2];
        while(n)
        {
            re=n%65536;
            if(re)
            {
                memset(tempa,0,sizeof(tempa));
                for(i=0;i<2;i++)
                    for(j=0;j<2;j++)
                        tempa[i]=(tempa[i]+base[u][re][i][j]*ans[j]%mod)%mod;
                for(i=0;i<2;i++)ans[i]=tempa[i]%mod;
            }
            u++;
            n/=65536;
        }
        return ans[0]%mod;
    }
    int main()
    {
        init();
        ll q,n,a,ans=-1;
        scanf("%lld%lld",&q,&n);
        for(int i=1;i<=q;i++)
        {
            if(n-1)a=jkpow(n-1);else a=0;
            n^=(a*a);
            if(ans==-1)ans=a;
            else ans^=a;
            if(!a)break;
        }
        printf("%lld
    ",ans);
    }

    上边是矩阵的

    我再贴一个整数的k进制快速幂。

    /*
    k进制快速幂
    对同一个基数来说 大数幂可以加快
    对不同基数 或者是 正常幂 很鸡肋 甚至有毒 
    
    */
    #include<bits/stdc++.h> 
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    const int maxn=1e2+50;
    const int inf=0x3f3f3f3f;
    ll kpow(ll a,ll b)
    {
        ll ans=1;
        while(b)
        {
            if(b&1)ans=ans*a%mod;
            a=a*a%mod;
            b>>=1;
        }
        return ans;
    }
    ll base[6][65540];
    //base[i][1]是基数a的i+1次方 
    //base[i][j]是base[i][1]的j倍。 
    void init(ll a,ll k)
    {
        base[0][1]=a%mod;
        int up=3;//up 根据最大数(龙龙)开k次方根的大小来估算 
        for(int i=1;i<=up;i++)base[i][0]=1;
        for(int i=0;i<=up;i++)
        {
            if(i)base[i][1]=base[i-1][k];
            for(int j=2;j<=k;j++)
            {
                base[i][j]=base[i][j-1]*base[i][1]%mod;
            }
        }
    }
    ll K65536_pow(ll a,ll b)
    {
        int k=65536;
        init(a,k);
        ll ans=1,i=0;
        while(b)
        {
            ans=ans*base[i++][b%k]%mod;
            b/=k;
        }
        return ans;
    }
     
    int main()
    {
        ll T;
        scanf("%d",&T);
        while(T--)
        {
            ll a,b;
            cin>>a>>b;
            cout<<kpow(a,b)<<" "<<K65536_pow(a,b)<<endl;
        }
    }

     2019-09-09 00:10:40

  • 相关阅读:
    20155322 2016-2017-2 《Java程序设计》第7周学习总结
    高级sed编程
    正则表达式
    实验十二:kvm环境下qemu-kvm创建虚拟机之间的网络配置
    实验十一:了解qemu-kvm的显示选项参数
    实验十:在kvm下创建winxp虚拟机
    实验九:第二个虚拟机的创建(标准选项)
    实验八:kvm环境搭建并创建第一台cirrOS虚拟机
    sed和gawk
    在脚本中添加颜色
  • 原文地址:https://www.cnblogs.com/kkkek/p/11487963.html
Copyright © 2020-2023  润新知