2025 Summer Day4
Content:Math
Date:2025.7.20
内容
- 矩阵
- 线性方程组
- 行列式
- 矩阵树定理
- 线性基
具体内容
矩阵
矩阵定义
定义
将一些元素排列成若干行,每行放上相同数量的元素,就是一个矩阵 (Matrix)。 对于矩阵 的第 行,第 列,我们记作 或 。 对于举证 ,如果 ,则我们称矩阵 为方阵。
矩阵基本操作
- 矩阵加法:对于矩阵 , , 我们定义矩阵加法为 即矩阵对应位置上的元素之和。
- 标量乘法:对于矩阵 和标量 , 我们定义矩阵的标量乘法为:
- 转置:对于矩阵 ,其转置为:
- 矩阵的拼接:对于矩阵 ,,其拼接为记为 ,其大小为 。
- 矩阵的乘法:对于矩阵 和矩阵 ,其矩阵乘法定义为:
+ 矩阵乘法具备分配律:$(A + B)C = AC + BC$;
+ 矩阵乘法具有结合律:$(AB)C = A(BC)$;- 矩阵乘法单位元: 矩阵的乘法单位元 为矩阵主对角线上全部为 ,其余均为 的 矩阵。
- 矩阵的逆:如果对于矩阵 存在矩阵 ,使得 ,则 称作矩阵 的逆元,记作 。 对于逆元我们可以使用高斯消元求解。我们对矩阵 进行高斯消元,最后会得到 (若无法把左边化为乘法单位元,则矩阵 不存在逆元),则 就为 的逆元。
矩阵与线性方程组
对于线性方程组
将未知数的系数写成矩阵的形式,用系数所在的矩阵的行表示未知数,我们就得到了线性方程组的矩阵 (增广矩阵) 表达形式:
对于线性方程组,我们通常会使用消元来求解。在矩阵表示下的线性方程组可以用高斯消元来求解。 高斯消元是通过对矩阵进行初等变换,以保证在方程的解不变的情况下求出方程的解。一般来说步骤如下 (记增广矩阵为 ):
- 枚举变量 () 表示当前要对未知元 进行消元操作。
- 寻找最大的 ,使得 ,交换 。
- 将交换后的矩阵的第 行的未知元 的系数化为 ,即: ()。
- 用矩阵的第 行对矩阵的第 () 行进行消元操作,即:。
- 重复上述操作直到 或者找不到满足条件 中的 。
最后的结果可能会有以下三种
- 如果 ,且存在 使得 ,则原线性方程组无解。
- 如果 ,且对于 满足 ,则原线性方程组有无数解。
- 否则原线性方程组有唯一解。
洛谷 P3389 Code
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 105;
constexpr double eps = 1e-8;
class Matrix {
double mat[N][N] = {{0}};
int size = 0;
public:
Matrix() = default;
void input_matrix() {
cin >> size;
for (int i = 0; i < size; i++)
for (int j = 0; j <= size; j++)
cin >> mat[i][j];
}
int guess() {
int i, j, r = 0;
for (int c = 0; c < size; c++) {
int t = r;
for (i = t + 1; i < size; i++) {
if (abs(mat[i][c]) > abs(mat[t][c])) {
t = i;
}
}
if (abs(mat[t][c]) < eps) continue;
for (i = c; i <= size; i++) {
swap(mat[t][i], mat[r][i]);
}
for (i = size; i >= c; i--) {
mat[r][i] /= mat[r][c];
}
for (i = r + 1; i < size; i++) {
if (abs(mat[i][c]) > eps) {
for (j = size; j >= c; j--) {
mat[i][j] -= mat[r][j] * mat[i][c];
}
}
}
r++;
}
if (r < size) {
return -1;
}
for (i = size - 1; i >= 0; i--) {
for (j = i + 1; j < size; j++) {
mat[i][size] -= mat[i][j] * mat[j][size];
}
}
return 1;
}
int get_size() const { return size; }
double get_solution(int i) const { return mat[i][size]; }
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
Matrix matrix;
matrix.input_matrix();
if (matrix.guess() == -1) {
cout << "No Solution\n";
} else {
for (int i = 0; i < matrix.get_size(); i++) {
double answer = matrix.get_solution(i);
if (abs(answer) < eps) {
answer = 0;
} else {
answer = round(answer * 100.00) / 100.00;
}
cout << setiosflags(ios::fixed) << setprecision(2) << answer << "\n";
}
}
return 0;
}洛谷 P2455 Code
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
const double eps = 1e-8;
int n;
double a[N][N];
int gauss() {
int i, j, c, r = 0;
for (c = 0; c < n; c ++) {
int t = r;
for (i = t + 1; i < n; i ++)
if (abs(a[i][c]) > abs(a[t][c])) t = i;
if (abs(a[t][c]) < eps) continue;
for (i = c; i <= n; i ++) swap(a[t][i], a[r][i]);
for (i = n; i >= c; i --) a[r][i] /= a[r][c];
for (i = r + 1; i < n; i ++)
if (abs(a[i][c]) > eps)
for (j = n; j >= c; j --)
a[i][j] -= a[r][j] * a[i][c];
r ++;
}
if (r < n) {
for (i = r; i < n; i ++)
if (abs(a[i][n]) > eps) return 2;
return 1;
}
for (i = n - 1; i >= 0; i --)
for (j = i + 1; j < n; j ++)
a[i][n] -= a[i][j] * a[j][n];
return 0;
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i ++) {
for (int j = 0; j <= n; j ++) {
scanf("%lf", &a[i][j]);
}
}
int t = gauss();
if (t == 2) puts("-1");
else if (t == 1) puts("0");
else {
for (int i = 0; i < n; i ++) {
printf("x%d=", i + 1);
if (abs(a[i][n]) < eps) a[i][n] = 0;
printf("%.2lf\n", a[i][n]);
}
}
return 0;
}除了高斯消元外,我们还有另外一种消元的方式——高斯-约旦消元,下面给出代码。
洛谷 P3389 Code
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 105;
constexpr double eps = 1e-8;
class Matrix {
double mat[N][N] = {{0}};
int size = 0;
public:
Matrix() = default;
void input_matrix() {
cin >size;
for (int i = 0; i < size; i++)
for (int j = 0; j <= size; j++)
cin >mat[i][j];
}
int guess() {
int i, j, r = 0;
for (int c = 0; c < size; c++) {
int t = r;
for (i = t + 1; i < size; i++) {
if (abs(mat[i][c]) abs(mat[t][c])) {
t = i;
}
}
if (abs(mat[t][c]) < eps) continue;
for (i = c; i <= size; i++) {
swap(mat[t][i], mat[r][i]);
}
for (i = size; i >= c; i--) {
mat[r][i] /= mat[r][c];
}
for (i = r + 1; i < size; i++) {
if (abs(mat[i][c]) eps) {
for (j = size; j >= c; j--) {
mat[i][j] -= mat[r][j] * mat[i][c];
}
}
}
r++;
}
if (r < size) {
return -1;
}
for (i = size - 1; i >= 0; i--) {
for (j = i + 1; j < size; j++) {
mat[i][size] -= mat[i][j] * mat[j][size];
}
}
return 1;
}
int get_size() const { return size; }
double get_solution(int i) const { return mat[i][size]; }
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
Matrix matrix;
matrix.input_matrix();
if (matrix.guess() == -1) {
cout << "No Solution\n";
} else {
for (int i = 0; i < matrix.get_size(); i++) {
double answer = matrix.get_solution(i);
if (abs(answer) < eps) {
answer = 0;
} else {
answer = round(answer * 100.00) / 100.00;
}
cout << setiosflags(ios::fixed) << setprecision(2) << answer << "\n";
}
}
return 0;
}行列式
定义
对于矩阵 (通常为方阵) ,我们定义其行列式为:
其中 表示长度为 的所有排列的集合, 表示 。 这里有一张快速求解行列式的图:

快速求解行列式
性质
- 对矩阵做行 (列) 交换,行列式反号。 根据排列的奇偶性,我们可以知道交换一个排列中的一对元素,其排列奇偶性也会发生变化,所以 ,证毕。
- 对矩阵做行 (列) 数乘,行列式乘上同样的常数 我们知道一个排列包含 之间的所有整数,所以被修改的元素会在每个连乘中出现且每个连乘中仅出现一次,所以可以提到整个式子的前面,证毕。
- 对矩阵做行 (列) 加法,行列式不变。
求解行列式
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 605;
int n;
long long p;
class Matrix {
long long mat[N][N] = {{}};
int size = 0;
public:
Matrix() = default;
void get_input(int n) {
size = n;
for (int i = 1; i <= size; i++) {
for (int j = 1; j <= size; j++) {
cin >mat[i][j];
}
}
}
long long det(const long long ModValue) {
long long retval = 1, sgn = 1;
for (int i = 1; i <= size; i++) {
for (int j = i + 1; j <= size; j++) {
while (mat[i][i]) {
long long divide = mat[j][i] / mat[i][i];
for (int k = 1; k <= size; k++) {
mat[j][k] = (mat[j][k] - divide * mat[i][k] % ModValue + ModValue) % ModValue;
}
sgn = -sgn;
swap(mat[i], mat[j]);
}
sgn = -sgn;
swap(mat[i], mat[j]);
}
}
retval = sgn;
for (int i = 1; i <= size; i ++) {
retval = retval * mat[i][i] % ModValue;
}
return (retval + ModValue) % ModValue;
}
} matrix;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >n >p;
matrix.get_input(n);
cout << matrix.det(p) << '\n';
return 0;
}矩阵树定理 (根本听不懂啊)
矩阵树定理是把图的生成树个数和矩阵行列式联系起来的一个定理。 矩阵树定理 对于无自环 (允许重边) 的有向图 。设出度矩阵 ,其中 ,表示点 的出度,其余位置全部为 。而 表示图 的邻接矩阵,其中 表示 的边的数量 ()。那么可以得到对应的拉普拉斯矩阵 。 关于 的余子式是以 为根节点的内向生成树的个数。
余子式 对于矩阵 , 的余子式定义为 去掉第 行第 列的矩阵行列式。
线性基
定义
在 维异或空间下,一个向量可以用一个 内的整数表示 (即该数在二进制意义下的每一位都表示一个向量)。 对于一组向量 :
- 中数字的异或和称为这些向量的线性组合。
- 若不存在非空子集 满足 ,则称 线性无关。
- 的所有子集的异或和的集合构成 的张成,记作 。
- 的线性基是一个线性无关的向量集合 ,满足 。
- 维空间的线性基 满足 。