【积累】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

原文地址:https://www.cnblogs.com/kkkek/p/11487963.html