矩阵乘法
别人的博客
1.矩阵
A. 基本概念
一个 \(n\times m\) 的矩阵表示 \(n\) 行 \(m\) 列 的一个矩阵。
如果两个矩阵相乘,那么 第一个矩阵的列数 应该与 第二个矩阵的行数 相同。
如果一个 \(2\times 3\) 的矩阵 乘上一个 \(3\times 4\) 的矩阵,那么就长成这个样子:
也就是变成了一个 \(2 \times 4\) 的矩阵。
但是数值是如何变化的呢?
现在给一个 \(a\times n\) 的 矩阵 \(A\) 和 \(n\times m\) 的 矩阵 \(B\)。
我们可以称 矩阵 \(A\) \(\times\) 矩阵 \(B\) \(=\) 矩阵 \(C\)(\(a\times m\))。
那么 \(C_{i, j} = \sum_{k = 1} ^ n A_{i, k} \times B_{k, j}\)
因为 矩阵 \(A\) 的列数 与 矩阵 \(B\) 的行数 相同,那么即 \(C_{i, j}\) 是矩阵 \(A\) 中 第 \(i\) 行的 第 \(k\) 列 与 矩阵 \(B\) 中第 \(k\) 行的 第 \(j\) 列 的乘积的和(\(k\in \begin{bmatrix} 1, n \end{bmatrix}\))
\(\,\)
B.单位矩阵
就是一个矩阵 \(A\),让它乘上一个矩阵,结果仍为矩阵 \(A\)。
假如有一个 \(n\times m\) 的矩阵 \(A\),那么它的单位矩阵是一个 \(m\times m\) 的,对角线上为 \(1\),其他位上为 \(0\) 的。
\(\,\)
C.性质
有结合律,如 \(A\times B\times C = A\times (B\times C)\),依靠这个,我们也可以用快速幂的样子优化了。
没有交换律,不然对不上了。所以写代码时顺序一定不能错了!
2.例题
Luogu - P3390 【模板】矩阵快速幂
$\color{Green}{点击查看代码}$
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" = "<<x<<endl;
const int N = 105, M = 1e9 + 7;
int n;
ll k;
inline ll read() {
ll s = 0, f = 1; char c = getchar();
while (c < '0' || c>'9') { if (c == '-')f = -1; c = getchar(); }
while (c >= '0' && c <= '9') { s = (s << 1) + (s << 3) + (c ^ 48); c = getchar(); }
return s * f;
}
struct Matrix {
ll val[N][N];
void init() { memset(val, 0, sizeof val); }
void build() { for (int i = 1; i <= n; i++)val[i][i] = 1; } //构造单位矩阵
Matrix operator *(Matrix tmp) { //重载运算符,当看到 Matrix 类型的 * 号时,会自动识别并运算
Matrix ans;
ans.init(); //一定要清空
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
ans.val[i][j] = (ans.val[i][j] + val[i][k] * tmp.val[k][j] % M) % M;
return ans;
}
}A, Ans;
int main() {
n = read(), k = read();
Ans.build();
for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)A.val[i][j] = read();
while (k) { //矩阵快速幂
if (k & 1)Ans = Ans * A;
A = A * A;
k >>= 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)cout << Ans.val[i][j] << ' ';
cout << endl;
}
return 0;
}
\(\,\)
再来看一看运用,Luogu - P1939 【模板】矩阵加速(数列)
别人的题解(为什么这样递推讲得挺清楚了)
我们可以认为这题的矩阵是长这个样子滴~ 也就是基本形式:
那么变化是这样子乘的:
那我们到时候重载一下运算符,直接写:
矩阵Ans = 矩阵Ans * 变化形式
这个东西乘一次变化矩阵下标是右移 \(1\) 的,那么乘 \(n-3\) 次,然后输出第一个元素就 OK 了。
$\color{Green}{点击查看代码}$
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" = "<<x<<endl;
const int N = 1e5 + 5, M = 1e9 + 7;
int t, n;
struct Matrix {
ll val[4][4];
void init() { memset(val, 0, sizeof(val)); }
void build() { for (int i = 1; i <= 3; i++)val[i][i] = 1; }
Matrix operator *(Matrix tmp) {
Matrix ans;
ans.init();
for (int i = 1; i <= 3; i++)
for (int j = 1; j <= 3; j++)
for (int k = 1; k <= 3; k++)
ans.val[i][j] = (ans.val[i][j] + val[i][k] * tmp.val[k][j] % M) % M;
return ans;
}
}A, Ans, base;//base(n. 基础) 为变化形式
inline int read() {
int s = 0, f = 1; char c = getchar();
while (c < '0' || c>'9') { if (c == '-')f = -1; c = getchar(); }
while (c >= '0' && c <= '9') { s = (s << 1) + (s << 3) + (c ^ 48); c = getchar(); }
return s * f;
}
void reset() {
A.init();
A.val[1][1] = A.val[1][2] = A.val[1][3] = 1;//初始值
Ans.init();
Ans.build();
base.init();
base.val[1][1] = base.val[1][2] = base.val[2][3] = base.val[3][1] = 1;
}
int main() {
t = read();
while (t--) {
n = read() - 3;
if (n <= 0) { printf("%d\n", 1); continue; }
reset();
while (n) { //矩阵快速幂
if (n & 1)Ans = Ans * base;
base = base * base;
n >>= 1;
}
A = A * Ans;
printf("%lld\n", A.val[1][1]);
}
return 0;
}
\(\,\)

浙公网安备 33010602011771号