矩阵乘法

别人的博客


1.矩阵

A. 基本概念

一个 \(n\times m\) 的矩阵表示 \(n\)\(m\) 列 的一个矩阵。

如果两个矩阵相乘,那么 第一个矩阵的列数 应该与 第二个矩阵的行数 相同。

如果一个 \(2\times 3\) 的矩阵 乘上一个 \(3\times 4\) 的矩阵,那么就长成这个样子:

\[\begin{bmatrix} - & - & -\\ - & - & - \end{bmatrix} \times \begin{bmatrix} - & - & - & - \\ - & - & - & - \\ - & - & - & -\end{bmatrix} = \begin{bmatrix} - & - & - & - \\ - & - & - & - \end{bmatrix} \]

也就是变成了一个 \(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 【模板】矩阵加速(数列)

别人的题解(为什么这样递推讲得挺清楚了)

我们可以认为这题的矩阵是长这个样子滴~ 也就是基本形式:

\[\begin{bmatrix} F_n & F_{n-1} & F_{n-2} \end{bmatrix} \]

那么变化是这样子乘的:

\[\begin{bmatrix} 1 & 0 & 1 \\ 1 & 0 & 0 \\ 0 & 1 & 0 \end{bmatrix} \]

那我们到时候重载一下运算符,直接写:

矩阵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;
}

\(\,\)

posted @ 2023-09-29 01:45  今添  阅读(37)  评论(0)    收藏  举报