矩阵快速幂算法+例题(HDU 5667 Sequence)

矩阵快速幂是ACM比赛中对于求递推式能用到的模板,能实现O(N^3*logM)的复杂度,其中 N是矩阵阶乘,M是要求的第几项。

对于矩阵快速幂,首先的得知道单位矩阵

111100101

明显,单位矩阵与任何矩阵相乘,都会得到那个矩阵,这个就是对矩阵快速幂的时候的基础矩阵。
然后,我们首先来看看斐波拉契数列
斐波拉契数列的递推式是Fn = Fn-1 + Fn-2
所以我们可以试着写出对应的矩阵算式有:
{An+1An}={1110}{AnAn1}

这样,我们就有:
{AnAn1}={1110}n{11}

所以,如果要求出递推式的那一项,就可以首先根据递推式写出递推矩阵,然后对矩阵进行快速幂,这样我们就能快速得到我们要求的那一项了,不过,有些题目要求取余一个mod,这个可以在矩阵惩罚乘法中实现。

最后,我在给出有点广泛应用的矩阵构造式:

f(x)=i=0m1BiAn+i

An+mAn+m1An=Bm1100B11B000An+m1An+m2An

现在,我们来看看这道例题

HDU 5667 Sequence

题目链接
这道题我们简单分析下就有:

An+1An1=c10100b01AnAn11(n>=2)

这个求出来只是指数上的,所以mod p的时候得 mod p-1(因为p是质数),然后对a快速幂,这时候就mod p就能得到答案了。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define LL long long
LL p,aa,bb,cc,n,mod; //mod为p-1
struct matrix
{
    LL mat[3][3];
};
matrix pow1(matrix a,matrix b) // N^3的矩阵相乘
{
    matrix c;
    memset(c.mat,0,sizeof(c.mat));
    for(int i=0;i<3;i++){
        for(int j=0;j<3;j++){
            for(int k=0;k<3;k++){
                c.mat[i][j] += (a.mat[i][k]*b.mat[k][j]);
                c.mat[i][j] %= mod;
            }
        }
    }
    return c;
}
matrix cheng(matrix a,LL y) //矩阵快速幂
{
    matrix b;
    memset(b.mat,0,sizeof(b.mat));
    for(int i=0;i<3;i++) b.mat[i][i] = 1;
    while(y){
        if(y&1){
            b = pow1(a,b);
            y-=1;
        }else {
            a = pow1(a,a);
            y/=2;
        }
    }
    return b;
}
LL quick_pow(LL a,LL tmp) //对a进行快速幂
{
    LL b = 1ll;
    while(tmp)
    {
        if(tmp&1) b=(a*b)%p;
        a=(a*a)%p;
        tmp>>=1;
    }
    return b;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%lld%lld%lld%lld%lld",&n,&aa,&bb,&cc,&p);
        matrix ma;
        mod = p-1;
        memset(ma.mat,0,sizeof(ma.mat));//初始化递归矩阵
        ma.mat[0][0] = cc; ma.mat[0][1] = 1; ma.mat[0][2] = bb;
        ma.mat[1][0] = 1; ma.mat[2][2] = 1;


        ma = cheng(ma,n-2); //算指数和直接幂有点不同
        LL tmp = ma.mat[0][0]*bb + ma.mat[0][2]; //取出指数
        LL ans = quick_pow(aa,tmp);
        printf("%lld\n",ans);

    }
    return 0;
}

代码是自己第一次写的,所以有点丑,如果有错误或者有冒犯,请联系作者。

参考文献:
挑战程序设计竞赛 人民邮电出版社

发布了40 篇原创文章 · 获赞 7 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章