• P1939 【模板】矩阵加速(数列)


    \(P1939\) 【模板】矩阵加速(数列)

    题目传送门

    一、题目描述

    20221101145111

    二、解题思路

    • 从左到右,从小到大 【推荐写法】
      20221101150914
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int MOD = 1e9 + 7;
    const int N = 4;
    int n;
    LL f[N][N], b[N][N];
    
    void init() {
        memset(f, 0, sizeof(f));
        f[1][1] = f[1][2] = f[1][3] = 1;
    
        // base数组,原理见题解
        memset(b, 0, sizeof(b));
        b[1][3] = b[2][1] = b[3][2] = b[3][3] = 1;
    }
    
    //矩阵乘法
    void mul(LL C[][N], LL A[][N], LL B[][N]) {
        LL t[N][N] = {0};
        for (int i = 1; i < N; i++)
            for (int j = 1; j < N; j++)
                for (int k = 1; k < N; k++)
                    t[i][j] = (t[i][j] + (A[i][k] * B[k][j] % MOD)) % MOD;
        memcpy(C, t, sizeof t);
    }
    //快速幂
    void qmi(int k) {
        while (k) {
            if (k & 1) mul(f, f, b);
            mul(b, b, b);
            k >>= 1;
        }
    }
    
    int main() {
        int T;
        cin >> T;
        while (T--) {
            cin >> n;
            if (n <= 3) {
                printf("1\n");
                continue;
            }
            init();
            qmi(n - 1);
            printf("%lld\n", f[1][1]);
        }
    }
    
    

    \(Q:\)为什么是\(n-1\)次幂?

    \(A:\)从小到大的初始矩阵,最初时,第一行第一列是\(f(1)\),它乘上一个\(base\)可以到达\(f(2)\),那么,需要到达\(f(n)\),当然是乘上\(n-1\)\(base\)矩阵了。

    • 从左到右,从大到小
      20221101150933
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int MOD = 1e9 + 7;
    const int N = 4;
    int n;
    LL f[N][N], b[N][N];
    
    void init() {
        memset(f, 0, sizeof(f));
        f[1][1] = f[1][2] = f[1][3] = 1;
    
        memset(b, 0, sizeof(b));
        b[1][1] = b[1][2] = b[2][3] = b[3][1] = 1;
    }
    
    void mul(LL C[][N], LL A[][N], LL B[][N]) {
        LL t[N][N] = {0};
        for (int i = 1; i < N; i++)
            for (int j = 1; j < N; j++)
                for (int k = 1; k < N; k++)
                    t[i][j] = (t[i][j] + (A[i][k] * B[k][j] % MOD)) % MOD;
        memcpy(C, t, sizeof t);
    }
    void qmi(int k) {
        while (k) {
            if (k & 1) mul(f, f, b);
            mul(b, b, b);
            k >>= 1;
        }
    }
    
    int main() {
        int T;
        cin >> T;
        while (T--) {
            cin >> n;
            if (n <= 3) {
                printf("1\n");
                continue;
            }
            init();
            qmi(n - 3);
            printf("%lld\n", f[1][1]);
        }
    }
    
    

    \(Q:\)为什么是\(n-3\)次幂?

    \(A:\)从大到小的初始矩阵,最初时,第一行第一列是\(f(3)\),它乘上一个\(base\)可以到达\(f(4)\),那么,需要到达\(f(n)\),当然是乘上\(n-3\)\(base\)矩阵了。

    三、疑问与解答

    \(Q1\):为什么我看到有人写的时候用的是一维的\(f\)数组,你是用的二维?有什么区别?

    \(A\):其实真正有用的确定是一维,我这里用的二维是因为我懒:矩阵快速幂的模板有多套,我自己整理了一个我喜欢的样式

    void mul(LL C[][N], LL A[][N], LL B[][N]) {
        LL t[N][N] = {0};
        for (int i = 1; i < N; i++)
            for (int j = 1; j < N; j++)
                for (int k = 1; k < N; k++)
                    t[i][j] = (t[i][j] + (A[i][k] * B[k][j] % MOD)) % MOD;
        memcpy(C, t, sizeof t);
    }
    void qmi(int k) {
        while (k) {
            if (k & 1) mul(f, f, b);
            mul(b, b, b);
            k >>= 1;
        }
    }
    

    网上网友提供的模板:

    • 通过函数重载写成两个\(mul\)函数的,一个用于计算\(A \times B\),然后结果保存到\(A\)中,另一个用于计算\(A \times A\),结果保存到\(A\)中,还特别支持了一维数组传入,这样背模板就太麻烦了,我不喜欢。
    • 通过声明一个结构体,重载乘法运算符,太繁琐,我也不喜欢。

    我的这个模板具有如下优点:

    • 支持二维相同,不同,矩阵相乘,结果可以保存回原数组,也可以赋值新数组
    • 代码简洁
    • 一维的矩阵,可以通过构造二维的方法传入,一样可以解决。

    四、遗留问题

    矩阵快速幂——从入门到放弃(看这一篇就够了)
    https://www.codetd.com/article/12024444

  • 相关阅读:
    PhotoshopCS6中文版图像处理实战从入门到精通
    Web安全开发指南
    OpenStack运维指南
    Word/Excel/PPT 2016高效办公实战从入门到精通
    UG NX 8.5中文版基础教程
    Moldflow 2018模流分析从入门到精通:升级版
    数据库与数据处理:Access 2010实现
    iOS开发网络数据之AFNetworking使用1
    AFNetworking2.5使用2
    iOS项目的完整重命名方法图文教程
  • 原文地址:https://www.cnblogs.com/littlehb/p/16039658.html
Copyright © 2020-2023  润新知