矩阵乘法与矩阵快速幂
1 矩阵乘法
1.1 定义
对于两个矩阵
例如:
则有:
1.2 注意点
- 只有当矩阵
满足 的列数等于 的行数时,才能进行矩阵乘法。 - 矩阵乘法不满足交换律(这点很重要)
1.3 代码
提供一个封装模板。
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
const int Maxn = 205;
int n, m, p;
struct matrix {
int n, m, p[Maxn][Maxn];
matrix operator * (const matrix &b) const {
matrix a = *this, c;
c.n = a.n, c.m = b.m;
for(int i = 1; i <= a.n; i++) {
for(int j = 1; j <= a.m; j++) {
for(int k = 1; k <= b.m; k++) {
c.p[i][k] += a.p[i][j] * b.p[j][k];
}
}
}
return c;
}
void print() {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
cout << p[i][j] << " ";
}
cout << '\n';
}
}
}A, B, C;
signed main() {
ios::sync_with_stdio(0);
cin >> n >> m;
A.n = n, A.m = m;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
cin >> A.p[i][j];
}
}
cin >> p;
B.n = m, B.m = p;
for(int i = 1; i <= m; i++) {
for(int j = 1; j <= p; j++) {
cin >> B.p[i][j];
}
}
C = A * B;
C.print();
return 0;
}
2 矩阵快速幂
2.1 定义
仿照着数字的快速幂,我们也能快速求出矩阵的幂。
定义矩阵的幂为:
这很明显需要满足原矩阵为正方形。
2.2 用途
矩阵快速幂的经典应用就是加速递推。
直接举几个例子。
2.2.1 「例 1」Fibonacci 第 n 项
Problem
求斐波那契数列第
Solution
第一眼:这不是递推板题吗!
然而
我们运用矩阵快速幂加速递推。
接下来我们来求一下递推式(注意掌握方法):
因此我们对于矩阵
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
const int Maxn = 205;
int n, mod;
struct matrix {
int n, m, p[Maxn][Maxn];
void init() {
n = 0, m = 0, memset(p, 0, sizeof p);
}
matrix operator * (const matrix &b) const {
matrix a = *this, c;
c.init();
c.n = a.n, c.m = b.m;
for(int i = 1; i <= a.n; i++) {
for(int j = 1; j <= a.m; j++) {
for(int k = 1; k <= b.m; k++) {
c.p[i][k] = (c.p[i][k] + a.p[i][j] * b.p[j][k] % mod) % mod;
}
}
}
return c;
}
matrix operator ^ (const int &o) const {
matrix res, a = *this; int b = o;
res.init();
res.n = res.m = 2;
res.p[1][1] = res.p[2][2] = 1;
while(b) {
if(b & 1) {
res = res * a;
}
a = a * a;
b >>= 1;
}
return res;
}
void print() {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
cout << p[i][j] << " ";
}
cout << '\n';
}
}
}A;
signed main() {
ios::sync_with_stdio(0);
cin >> n >> mod;
A.p[1][1] = A.p[1][2] = A.p[2][1] = 1;
A.n = A.m = 2;
A = A ^ (n - 1);
cout << A.p[1][1];
return 0;
}
2.2.2 「例 2」P207 迷路
Solution
我们发现如果边权为
但是此时边权不为
但是我们又会发现这个边权居然最高才
将一个点拆成
对于这样一条边,我们将
这样从
注意最后统计答案时统计的是点
接下来注意细节即可。
Code
#include <bits/stdc++.h>
/*
我们发现如果边权为 1,那就是一道求 t 次方的大板子题
然而这题有边权,所以不能这么干
我们考虑分层图的思想,既然边权才 9,那我们直接将一个点暴力拆成九个点,然后边权就能转化为 1
这下就是大板子题了,在求 t 次方即可
*/
using namespace std;
typedef long long LL;
const int Maxn = 205;
const int Mod = 2009;
int n, t, m;
struct matrix {
int n, m, p[Maxn][Maxn];
void init() {
n = 0, m = 0, memset(p, 0, sizeof p);
}
matrix operator * (const matrix &b) const {
matrix a = *this, c;
c.init();
c.n = a.n, c.m = b.m;
for(int i = 1; i <= a.n; i++) {
for(int j = 1; j <= a.m; j++) {
for(int k = 1; k <= b.m; k++) {
c.p[i][k] = (c.p[i][k] + a.p[i][j] * b.p[j][k] % Mod) % Mod;
}
}
}
return c;
}
matrix operator ^ (const int &o) const {
matrix res, a = *this; int b = o;
res.init();
res.n = res.m = a.n;
for(int i = 1; i <= n; i++) {
res.p[i][i] = 1;
}
while(b) {
if(b & 1) {
res = res * a;
}
a = a * a;
b >>= 1;
}
return res;
}
void print() {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
cout << p[i][j] << " ";
}
cout << '\n';
}
}
}A;
signed main() {
ios::sync_with_stdio(0);
cin >> n >> t;
m = n * 9;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= 8; j++) {
A.p[9 * (i - 1) + j][9 * (i - 1) + j + 1] = 1;
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
char ch;
cin >> ch;
if(ch > '0') {
A.p[9 * (i - 1) + ch - '0'][9 * (j - 1) + 1] = 1;
}
}
}
A.n = A.m = m;
A = A ^ t;
cout << A.p[1][9 * (n - 1) + 1];
return 0;
}
2.2.3 「例 3」佳佳的 Fibonacci
Problem
求
Solution
我们还是运用矩阵快速幂。
但是我们发现这个形式似曾相识,还记得求区间修改区间查询的 BIT 时候的式子吗?他和这个形式是一样的。
我们还是再来推一遍:
先设
然后有:
然后我们再令
接下来我们递推
还是分步讨论:
然后实现即可。
Code
十年 OI 一场空,不开 long long 见祖宗。
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
const int Maxn = 205;
int n, mod;
struct matrix {
int n, m, p[Maxn][Maxn];
void init() {
n = m = 0;
memset(p, 0, sizeof p);
}
void print() {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
cout << p[i][j] << " ";
}
cout << '\n';
}
}
matrix operator * (const matrix &b) const {
matrix a = *this, c;
c.init();
c.n = a.n, c.m = b.m;
for(int i = 1; i <= a.n; i++) {
for(int j = 1; j <= a.m; j++) {
for(int k = 1; k <= b.m; k++) {
c.p[i][k] = (c.p[i][k] + a.p[i][j] * b.p[j][k] % mod) % mod;
}
}
}
return c;
}
matrix operator ^ (const int &b) const {
int p = b;
matrix a = *this, res;
res.init();
res.n = res.m = a.n;
for(int i = 1; i <= a.n; i++) {
res.p[i][i] = 1;
}
while(p) {
if(p & 1) {
res = res * a;
}
a = a * a;
p >>= 1;
}
return res;
}
}A, F;
signed main() {
ios::sync_with_stdio(0);
cin >> n >> mod;
A.init(), F.init();
A.n = A.m = 4;
A.p[1][1] = A.p[1][2] = A.p[2][2] = A.p[2][3] = A.p[3][3] = A.p[3][4] = A.p[4][3] = 1;
F.n = 4, F.m = 1;
F.p[3][1] = 1;
A = A ^ n;
A = A * F;
cout << (n * A.p[2][1] % mod - A.p[1][1] + mod) % mod;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律