定義
在數學中,矩陣(Matix) 是一個按照長方形陣列排列的復數或實數集合。
由 \(m \times n\) 個數 \(a_{i,j}\) 排成的 \(m\) 行 \(n\) 列的數表稱為 \(m\) 行 \(n\) 列的矩陣,簡稱 \(m \times n\) 矩陣。(其中 \(m\) 為行數、\(n\) 為列數)
幾種常見矩陣類型
實矩陣
數表中多有元素均為實數
復矩陣
數表中所有元素均為復數
行矩陣
數表中只有一行元素,沒有列元素
列矩陣
數表中只有一列元素,沒有行元素
負矩陣
數表中所有元素均為負數
方陣
一種特殊的矩陣,行數 = 列數
單位陣
一種特殊的方陣,數表中從左上角到右下角的對角線(稱為主對角線)上的元素均為 \(1\) ,除此以外全都為 \(0\) 。
記作 \(I_n\) 或 \(E_n\) ,通常用 \(I\) 或 \(E\) 表示。
根據單位矩陣的特點,任何矩陣與單位矩陣相乘都等於本身。
同型矩陣
行數和列數分別相同的兩個矩陣。
若有
則
那么顯然,兩個矩陣互為同型矩陣是兩個矩陣相同的充分條件。
特殊矩陣
單元素矩陣
當一個矩陣的數表中只有一個元素時,可以不加括號。
但要注意,一個矩陣 \(5\) 和一個數字 \(5\) 的數學意義是不同的。
零矩陣
數表中所有元素均為零。零矩陣可直接表示為 \((0)\) 或者 \(0\)。
但是值得注意的是,兩個零矩陣不一定是同型矩陣,更不一定是相同矩陣,例如
與
既不是同型矩陣,更不是相同矩陣。
矩陣的對角線
注意,只有方陣才能有對角線,即只有當矩陣的 行數 = 列數 時才能有對角線。
錯誤示范:

正確示范:

矩陣運算
加減法
將兩個矩陣的每一個元素相加減,對應元素形成的結果作為新矩陣的對應元素。
注意:兩矩陣能夠做加減法運算的充要條件是這兩個矩陣是同型矩陣。
矩陣乘法
數乘矩陣
用一個數乘一個矩陣等於用一個數乘以這個矩陣的全部元素。
矩陣相乘
設 A 為 \(m \times p\) 的矩陣,B 為 \(p \times n\) 的矩陣,那么稱 \(m \times n\) 的矩陣 C 為矩陣 A 與 B 的乘積,記作 \(C = A \times B\),其中矩陣 C 中的第 \(i\) 行第 \(j\) 列元素可以表示為:
計算過程:
若公式顯示不完整可以點擊這里查看。
運算律
-  
數乘結合律: \(K(AB)=(KA)B=A(KB)\)
 -  
乘法結合律: \((AB)C=A(BC)\)
 -  
乘法左分配律: \((A+B)C=AC+BC\)
 -  
乘法右分配律: \(C(A+B)=CA+CB\)
 
注意:
若 \(A \times B\) 有意義, \(B \times A\) 不一定有意義。
若 \(A \times B=B \times A\) ,則說明 \(A\) 和 \(B\) 是可交換的。
矩陣加速
P1962 斐波那契數列
一道非常基礎的矩乘矩陣加速模板題呢~
注意到數據范圍是 \(1 \leq n \leq 2^{63}\) ,肯定不能直接進行常規遞推求解。
設
-  
\(Fib(n)\) 表示矩陣 \(\begin{bmatrix}F_n & F_{n-1}\end{bmatrix}\) ;
 -  
\(base\) 使得 \(Fib(n-1) \times base = Fib(n)\) ,即 \(\begin{bmatrix}F_{n-1} & F_{n-2}\end{bmatrix} \times base = \begin{bmatrix}F_n & F_{n-1}\end{bmatrix}\) 。
 
斐波那契數列的遞推式為 \(F_n = F_{n-1} + F_{n-2}\) 。
如果要使得矩陣進行乘法時能令 \(F_{n-1}\) 與 \(F_{n-2}\) 相加,得出 \(F_n\),那么 \(base\) 矩陣的第一列一定為 \(\begin{bmatrix}1 \\ 1\end{bmatrix}\) 。
同理,如果要計算 \(F_{n-1}\) ,\(base\) 矩陣的第二列一定為 \(\begin{bmatrix}1 \\ 0 \end{bmatrix}\) 。
綜上所述,
原式可化為
代碼:
/*
Name: P1962 斐波那契數列
Solution: 矩陣加速
   
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Kmod 1000000007
#define int long long
using namespace std;
/*==================================================快讀*/
inline int read()
{
    int X = 0, F = 1;
    char CH = getchar();
    while (CH < '0' || CH > '9')
    {
        if (CH == '-')
            F = -1;
        CH = getchar();
    }
    while (CH >= '0' && CH <= '9')
    {
        X = (X << 3) + (X << 1) + (CH ^ 48);
        CH = getchar();
    }
    return X * F;
}
/*===============================================定義變量*/
int n;
struct Matrix
{
    int z[3][3];
    void init()
    {
        memset(z, 0, sizeof z);
    }
} base, ans;
/*=============================================自定義函數*/
void prepare()
{
    ans.init();
    ans.z[1][1] = 1;
    ans.z[1][2] = 1;
    base.init();
    base.z[1][1] = 1;
    base.z[1][2] = 1;
    base.z[2][1] = 1;
}
Matrix mul(Matrix a, Matrix b)
{
    Matrix res;
    res.init();
    for (int i = 1; i <= 2; ++i)
        for (int j = 1; j <= 2; ++j)
            for (int k = 1; k <= 2; ++k)
                res.z[i][j] = (res.z[i][j] + a.z[i][k] * b.z[k][j]) % Kmod;
    return res;
}
void Qpow(int b)
{
    while (b)
    {
        if (b & 1)
            ans = mul(ans, base);
        base = mul(base, base);
        b >>= 1;
    }
}
/*=================================================主函數*/
signed main()
{
    prepare();
    n = read();
    // if (n <= 2)
    // {
    //     printf("1\n");
    //     return 0;
    // }
    Qpow(n - 1);
    printf("%lld\n", ans.z[1][2] % Kmod);
    return 0;
}
 
        感謝巨佬 @KnightL 為代碼優化做出的貢獻!!
P1939 【模板】矩陣加速(數列)
上一題的推廣版,思路相同。
代碼:
/*
Name: P5550 Chino的數列
Solution: 
   
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
// #define int long long
using namespace std;
/*==================================================快讀*/
inline int read()
{
    int X = 0, F = 1;
    char CH = getchar();
    while (CH < '0' || CH > '9')
    {
        if (CH == '-')
            F = -1;
        CH = getchar();
    }
    while (CH >= '0' && CH <= '9')
    {
        X = (X << 3) + (X << 1) + (CH ^ 48);
        CH = getchar();
    }
    return X * F;
}
/*===============================================定義變量*/
int n, s, m, k;
struct Matrix
{
    int z[100][100];
    Matrix()
    {
        memset(z, 0, sizeof z);
    }
} base, ans;
/*=============================================自定義函數*/
inline Matrix mul(Matrix a, Matrix b)
{
    Matrix res;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            for (int k = 1; k <= n; k++)
                res.z[i][j] += a.z[i][k] * b.z[k][j];
    return res;
}
inline Matrix Qpow(Matrix a, int p)
{
    Matrix res;
    for (int i = 1; i <= n; i++)
        res.z[i][i] = 1;
    while (p)
    {
        if (p & 1)
            res = mul(res, a);
        a = mul(a, a);
        p >>= 1;
    }
    return res;
}
/*=================================================主函數*/
signed main()
{
    n = read();
    s = read();
    m = read();
    k = read();
    for (int i = 1; i <= n; i++)
    {
        ans.z[1][i] = read();
        base.z[i % n + 1][i] = 1;
    }
    swap(base.z[s], base.z[m]);
    ans = mul(ans, Qpow(base, k));
    for (int i = 1; i <= n; i++)
        printf("%d ", ans.z[1][i]);
    puts("");
    return 0;
}
 
       