定义
在数学中,矩阵(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;
}