快速幂 矩阵快速幂

快速幂取模:

原理:合并相同项,减少计算

计算(a^b)%c当b很大的时候用,朴素算法复杂度为n,快速幂复杂度为logn

以下为二进制优化后的代码:


ll quick_pow_mod(ll a,ll b,ll c)
{
    ll res=1;
    while(b)
    {
        if(b & 1)
        {
            res=(res*a)%c;
        }//
        a=(a*a)%c;
        b=b>>1;
    }
    return res;
}

 

快速幂有两个作用 一是降低复杂度,二是防止数据过大溢出。

 

 

同理,也有快速乘,但是我觉得这个玩意用的机会应该很少...

ll quick_mul(ll a,ll b,ll c)//类比快速幂
{
    ll res=0;
    while(b)
    {
        if(b&1)
        {
            res=(res+a)%c;
        }
        a=(a+a)%c;
        b=b>>1;
    }
    return res;
}

 

矩阵快速幂取模:

矩阵乘法和快速幂的结合,可以快速计算矩阵a的n次幂,把快速幂中的乘法改成矩阵乘法就行。

ps:我就先默认矩阵为二阶方阵了,其他情况改成N就好。

 

struct mat
{
    ll a[2][2];
    mat()
    {
        memset(a,0,sizeof(mat));//这一步不要忘记
    }
};

mat multiple(mat x,mat y)//模拟矩阵乘法,返回res矩阵
{
    mat res;
    for(int i=0;i<2;i++)//枚举res的行
    {
        for(int j=0;j<2;j++)//枚举res的列
        {
            for(int k=0;k<2;k++)
            {
                res.a[i][j]=((res.a[i][j]%mod)+((x.a[i][k]*y.a[k][j])%mod))%mod;
            }
        }
    }
    return res;
}

mat quick_pow(mat base,int n)
{
    mat ans;
    for(int i=0;i<2;i++)
    {
        ans.a[i][i]=1;//初始化为单位矩阵
    }
    while(n)
    {
        if(n&1)
        {
            ans=multiple(ans,base);//如果n这一位上为1,更新ans
        }
        base=multiple(base,base);//更新base
        n=n>>1;
    }
    return ans;
}

应用:

如斐波拉切数列:已知f(n)=f(n-1)+f(n-2),由矩阵乘法可以通过中间矩阵,找到f(n)和已知项的关系

 

 

然后只要对转移矩阵快速幂就行了。

 

贴一道UVA-10689,斐波拉切数列的变形,给定前两项,求第n项对一个数取模,矩阵快速幂模版题

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
typedef long long ll;
using namespace std;
int mod;
int a,b,n,m;//第一项为a 第二项为b 求第n项的后m位

struct mat
{
    ll a[2][2];
    mat()
    {
        memset(a,0,sizeof(mat));
    }
};

mat multiple(mat x,mat y)//模拟矩阵乘法,返回值res仍为矩阵
{
    mat res;
    memset(res.a,0,sizeof(res.a));
    for(int i=0;i<2;i++)//枚举行
    {
        for(int j=0;j<2;j++)//枚举列
        {
            for(int k=0;k<2;k++)
            {
                res.a[i][j]=((res.a[i][j]%mod)+((x.a[i][k]*y.a[k][j])%mod))%mod;
            }
        }
    }
    return res;
}

ll quick_power(int n)
{
    mat base,res;
    base.a[0][0]=1;
    base.a[0][1]=1;
    base.a[1][0]=1;
    base.a[1][1]=0;
    //memset(res.a,0,sizeof(res.a));
    for(int i=0;i<2;i++)
    {
        res.a[i][i]=1;//先初始化res矩阵为二维单位阵,类似于求乘积时初始化res为1
    }
    while(n)//快速幂
    {
        if(n&1)
        {
            res=multiple(res,base);
        }
        base=multiple(base,base);
        n=n>>1;
    }
    return (((res.a[0][0]*b)%mod+((res.a[0][1]*a)%mod))%mod);
}

int main()
{

    int t;
    cin>>t;
    while(t--)
    {
        cin>>a>>b>>n>>m;
        mod=(int)pow(10,m);
        if(n!=0)
        {
            ll result = quick_power(n-1);
            cout << result << endl;
        }
        else
            cout<<a<<endl;
    }
    return 0;
}

 

 

-----------------------

更新:以10为底数的快速幂,应对指数很大很大的情况

函数里的变量依次为底数 指数 模数


ll tenth_pow(ll a,string c,ll mod)
{
    ll t=c.size();t--;
    ll ans=1,s=a;
    while(t>=0)
    {
        ll cnt=c[t]-'0',cur=s;
        for(int i=1;i<=cnt;i++)
        {
            ans=ans*s%mod;
        }
        for(int i=1;i<10;i++)
        {
            cur=cur*s%mod;
        }
        s=cur;
        ans%=mod;
        t--;
    }
    return ans;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值