定義
在數學中,矩陣(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;
}