P3390 【模板】矩阵快速幂


题目传送门

一、复习整数快速幂

https://www.jianshu.com/p/749b6ee1d001

#include 

using namespace std;

//栗子: 2^11 = 2 ^ (1011)2
//就是把11拆成二进制数位,然后从右到左进行次方base的迭乘
//在从右到左的枚举过程中,发现本位是1,就乘上本位的base
//原来需要2*2*...*2,共10次,即O(N)
//现在需要log以2为底11就是log_2{11} 即4次
int qmi(int a, int b) {
    int ans = 1;
    while (b) {
        if (b & 1) ans *= a;
        a *= a; //次方base
        b >>= 1;
    }
    return ans;
}

int main() {
    cout << qmi(2, 11) << endl;
    return 0;
}

二、单位矩阵

在普通的乘法中,一个数乘\(1\)还是等于它本身,在矩阵乘法中也有这么一个“\(1\)”,它就是单位矩阵

不同于普通乘法中的单位\(1\),对于不同矩阵他们的单位矩阵大小是不同的

对于\(n*m\)的矩阵,它的单位矩阵大小为\(m*m\),对于\(m*n\)的矩阵,它的单位矩阵大小为\(n*n\)

也就是说单位矩阵都是正方形的,这是因为只有正方形的矩阵能保证结果和前一个矩阵形状相同

单位矩阵的元素非\(0\)\(1\),从左上角到右下角的对角线上元素皆为\(1\),其他皆为\(0\)

三、模板解法I

#include 
using namespace std;
//矩阵快速幂
typedef long long LL;
const int MOD = 1e9 + 7;
const int N = 110;
LL n, k; //本题数据范围很大,用int直接wa哭了

//矩阵声明
struct JZ {
    LL m[N][N];
} A, res, base;

//矩阵乘法
inline JZ mul(JZ A, JZ B) {
    JZ C;
    memset(C.m, 0, sizeof(C.m));
    for (LL i = 0; i < n; i++)
        for (LL j = 0; j < n; j++)
            for (LL k = 0; k < n; k++) {
                C.m[i][j] += (A.m[i][k] % MOD) * (B.m[k][j] % MOD);
                C.m[i][j] %= MOD;
            }
    return C;
}

void qmi() {
    //将结果矩阵初始化为单位矩阵
    memset(res.m, 0, sizeof res.m);
    for (int i = 0; i < n; i++) res.m[i][i] = 1;
    //其实就是把整数快速幂修改为矩阵快速幂
    while (k) {                       //二进制快速幂
        if (k & 1) res = mul(res, A); // 联想一下整数快速幂
        A = mul(A, A);                // base 翻倍
        k >>= 1;
    }
}
int main() {
    cin >> n >> k;
    //输入原始矩阵
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            cin >> A.m[i][j];

    //计算矩阵快速幂
    qmi();

    //输出
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++)
            cout << res.m[i][j] << " ";
        cout << endl;
    }
    return 0;
}

四、模板解法II

#include 
using namespace std;
//矩阵快速幂
typedef long long LL;
const int MOD = 1e9 + 7;
const int N = 110;
LL n, k; //本题数据范围很大,用int直接wa哭了
LL C[N][N], A[N][N], B[N][N];

//矩阵乘法
void mul(LL C[][N], LL A[][N], LL B[][N]) {
    //为什么非得要开一个临时的tmp来存结果呢,直接用C确实不行,原理不清楚
    static LL tmp[N][N];
    memset(tmp, 0, sizeof tmp);
    for (LL i = 0; i < n; i++)
        for (LL j = 0; j < n; j++)
            for (LL k = 0; k < n; k++) {
                tmp[i][j] += A[i][k] * B[k][j] % MOD; //矩阵乘法
                tmp[i][j] %= MOD;
            }
    memcpy(C, tmp, sizeof tmp);
}

void qmi() {
    //将结果矩阵初始化为单位矩阵
    memset(B, 0, sizeof B);
    for (int i = 0; i < n; i++) B[i][i] = 1;
    //其实就是把整数快速幂修改为矩阵快速幂
    while (k) { //二进制快速幂
        if (k & 1) mul(B, B, A); // 联想一下整数快速幂
        mul(A, A, A);            // base 翻倍
        k >>= 1;
    }
}
int main() {
    cin >> n >> k;
    //输入原始矩阵
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            cin >> A[i][j];

    //计算矩阵快速幂
    qmi();

    //输出
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++)
            cout << B[i][j] << " ";
        cout << endl;
    }
    return 0;
}