【OI】矩阵快速幂

经过漫长(并不务正业)的暑假,我终于回归了OI。

下面以此代码为例子:Luogu4910

#include <cstdio>
#include <cstring>

typedef long long ll;

const ll MOD = 1000000007;

ll fbi[5][5] = {{1,1,0,0,0},
                 {1,0,0,0,0},
                 {0,0,0,0,0},
                 {0,0,0,0,0},
                 {0,0,0,0,0}
                };
                
ll fir[5][5] = {{1,1,0,0,0},
                 {0,0,0,0,0},
                 {0,0,0,0,0},
                 {0,0,0,0,0},
                 {0,0,0,0,0}
                };

struct mat{
    ll a[5][5];
    int m,n;//行数,列数 
    
    mat(ll x[5][5],int m1,int n1){
        for(int i = 0 ; i < m1; i ++){
            for(int j = 0 ; j < n1 ; j++){
                a[i][j] = x[i][j];
            }
        }
        m = m1;
        n = n1;
    }
    mat(int m1, int n1, bool one){
        for(int i = 0; i < m1; i ++){
            for(int j = 0 ; j < n1 ; j++){
                a[i][j] = 0;
                if(i == j && one == true) a[i][j] = 1;
            }
        }
        m = m1;
        n = n1;
    }
};

ll FIB(int N);
void print(mat a);

mat matmul(mat a,mat b)
{

    if(a.n != b.m) return mat(5,5,false);     
    mat ret(a.m,b.n,false);
    for(int i = 0 ; i < a.m; i ++){
        for(int j = 0; j < b.n; j ++){
            for(int k = 0; k < a.n; k++){
                ret.a[i][j] += a.a[i][k] * b.a[k][j]; 
                ret.a[i][j] %= MOD;
            }
        }
    }
    return ret;
}
int n;  

mat poww(mat a,int b){
    mat ret(2,2,true);
    while(b){
        if(b&1 == 1) ret = matmul(a,ret);
        a = matmul(a,a);
        b>>=1;
    }
    return ret;
}

mat calc(int N){
    if(N == 1 || N == 2) return mat(1,2,true);
    mat firmat(fir,1,2);
    mat fb(fbi,2,2);
    mat res = poww(fb,N-1);
    mat ret = matmul(firmat,res);
    return ret;
}

void print(mat a){
    printf("--------printf--------\n");
    for(int i = 0 ; i < a.m ; i ++){
        for(int j = 0;  j < a.n; j ++){
            printf("%d ",a.a[i][j]);
        }
        printf("\n");
    }
    printf("---------end----------\n");
}

ll getans(int N){
    if(N == 1) return 2;
    mat M = calc(N-1);
    //print(M);
    return ((M.a[0][1]+M.a[0][0])%MOD+M.a[0][1])%MOD;
    //return FIB(n+1)+FIB(n-1);
}

int t;

ll FIB(int N){
    ll a = 1;
    ll b = 1;
    if(N < 3) return 1;
    for(int i = 3; i <= N; i++){
        ll tmp = b;
        b = (a+b)%MOD;
        a = tmp;
    }
    return b;
}
int main()
{
    scanf("%d",&t);
    //calc(t);
    for(int i = 0 ; i < t ; i++){
        scanf("%d",&n);
        printf("%lld\n",getans(n));
    }
    /* test /* 
    n = 1000000000;
    printf("fast:\n");
    printf("%lld\n",calc(n));
    printf("slow:\n");
    printf("%lld\n",FIB(n));
    */
    
    return 0;
}

所谓矩阵快速幂,就是快速幂和矩阵的合体。传统的快速幂是将一个数分为a^1 * a^2 * a^4...,从而进行快速幂计算。而矩阵快速幂,求的是矩阵的幂,由于矩阵乘法也满足类似性质,所以只要给定幂次,再将幂次分解,线性递推出就能矩阵的二的n次幂的幂,用相同于快速幂的方法即可求解。对于一些数论计算和递推题目可以利用矩阵快速幂优化。

posted @ 2020-09-18 21:33  dudujerry  阅读(186)  评论(0编辑  收藏  举报