快速幂 矩阵快速幂

快速幂取模:

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

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

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

ll quick_pow_mod(ll a,ll b,ll c)//二进制思想 二分乘  a^b=(a^2)^(b-1) 递推
{
    ll res=1;//乘法初始化为1
    while(b)
    {
        if(b & 1)//判断b的二进制最后一位是否为1(&运算同为1则取1)
        {
            res=(res*a)%c;//每次乘都取一次模,防止数据溢出 如果二进制的b这一位上为1,更新res
        }//
        a=(a*a)%c;//b每移一位,更新一次a,相当于乘2
        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就好。

扫描二维码关注公众号,回复: 919832 查看本文章


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;
}

这种题最关键的还是构造矩阵,模版题ok,题目灵活了我大概率就gg了。

加油,数论第二步。

猜你喜欢

转载自blog.csdn.net/neuq_zsmj/article/details/79520561