数学II-线性代数
前言
注:本文一切代码建立在此基础上
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll=long long;
using PI=pair<int,int>;
struct IO
{
char rbuf[1<<20],wbuf[1<<20],*p,*pp,*wp;
IO():p(rbuf),pp(rbuf),wp(wbuf){}
#define gc() (p==pp&&(pp=rbuf+fread(rbuf,1,1<<20,stdin),p=rbuf),p==pp?EOF:*p++)
#define pc(c) do{if(wp-wbuf==(1<<20))flush();*wp++=(c);}while(0)
IO&operator<<(char c){pc(c);return*this;}
IO&operator<<(const char*s){while(*s)pc(*s++);return*this;}
IO&operator>>(char*s){char c=gc();while(c<=32)c=gc();while(c>32)*s++=c,c=gc();*s='\0';return*this;}
template<typename T>IO&operator>>(T&x){x=0;char c=gc();bool f=0;while(c<'0'||c>'9')f|=(c=='-'),c=gc();while(c>='0'&&c<='9')x=x*10+(c^48),c=gc();if(f)x=-x;return*this;}
template<typename T>IO&operator<<(T x){if(x<0)pc('-'),x=-x;char t[30];int c=0;do{t[c++]=x%10+'0';}while(x/=10);while(c--)pc(t[c]);return*this;}
void flush(){if(wp>wbuf)fwrite(wbuf,1,wp-wbuf,stdout),wp=wbuf;}
~IO(){flush();}
}io;
#define cin io
#define cout io
一、矩阵基本概念与运算
1.1 矩阵的定义
定义:一个 \(m\times n\) 的矩阵是一个由 \(m\) 行 \(n\) 列元素排列成的矩形阵列,通常记作
也可简记为 \(A=(a_{ij})_{m\times n}\)。
- 当 \(m=n\) 时,称为方阵。
- 零矩阵:所有元素均为 \(0\),记作 \(O\)。
- 单位矩阵:\(I_n\) 或简记为 \(I\),是 \(n\) 阶方阵,其主对角线元素为 \(1\),其余为 \(0\)。
- 对角矩阵:非主对角线元素全为 \(0\) 的方阵。
1.2 矩阵的加减法与数乘
定义:
- 加法:设 \(A,B\) 均为 \(m\times n\) 矩阵,则 \(C=A+B\) 满足 \(c_{ij}=a_{ij}+b_{ij}\)。
- 数乘:设 \(k\) 为标量,则 \(kA\) 满足 \((kA)_{ij}=k\cdot a_{ij}\)。
性质(加法交换律、结合律,数乘分配律等)显然成立。
1.3 矩阵乘法
定义:设 \(A\) 为 \(m\times n\) 矩阵,\(B\) 为 \(n\times p\) 矩阵,则乘积 \(C=AB\) 是一个 \(m\times p\) 矩阵,其中
性质:
- 结合律:\((AB)C=A(BC)\)(若可乘)。
- 分配律:\(A(B+C)=AB+AC\),\((A+B)C=AC+BC\)。
- 一般不满足交换律:\(AB\neq BA\) 通常不成立。
- 单位矩阵 \(I\) 满足 \(AI=A\),\(IB=B\)。
1.4 矩阵的转置
定义:\(A^T\) 是将 \(A\) 的行列互换得到的矩阵,即 \((A^T)_{ij}=a_{ji}\)。
性质:
- \((A^T)^T=A\)。
- \((A+B)^T=A^T+B^T\)。
- \((kA)^T=kA^T\)。
- \((AB)^T=B^TA^T\)。
1.5 矩阵乘法的代码实现
对于 \(n\times n\) 方阵,矩阵乘法的朴素实现为 \(O(n^3)\)。在竞赛中常使用重载运算符。
const int MOD=1e9+7;
struct Mat
{
int n,m;
vector<vector<ll>> a;
Mat(int _n=0,int _m=0):n(_n),m(_m)
{
a.resize(n,vector<ll>(m,0));
}
Mat operator*(const Mat& b) const
{
Mat res(n,b.m);
for(int i=0;i<n;i++)
for(int k=0;k<m;k++)
if(a[i][k]) // 稀疏优化,可省略
for(int j=0;j<b.m;j++)
res.a[i][j]=(res.a[i][j]+a[i][k]*b.a[k][j])%MOD;
return res;
}
};
注意:循环顺序采用 i-k-j 可提高缓存命中率(优化常数)。
二、行列式
2.1 行列式的定义
定义:对于 \(n\) 阶方阵 \(A\),其行列式 \(\det(A)\) 或 \(|A|\) 是一个标量,定义为
其中 \(S_n\) 是所有 \(n\) 元排列的集合,\(\operatorname{sgn}(\sigma)\) 是排列的符号(偶排列为 \(+1\),奇排列为 \(-1\))。
2.2 行列式的基本性质
- 单位矩阵:\(\det(I)=1\)。
- 转置不变:\(\det(A^T)=\det(A)\)。
- 交换两行:行列式变号。
- 某行乘以 \(k\):行列式乘以 \(k\)。
- 某行加上另一行的倍数:行列式不变。
- 行列式按行展开(拉普拉斯展开):\(\det(A)=\sum_{j=1}^n (-1)^{i+j}a_{ij}M_{ij}\),其中 \(M_{ij}\) 是去掉第 \(i\) 行第 \(j\) 列后的子式。
- 乘积定理:\(\det(AB)=\det(A)\det(B)\)。
2.3 行列式的计算:高斯消元
通过高斯消元将矩阵化为上三角形式,同时记录行交换次数和行倍乘因子,即可得到行列式。具体算法:
- 对矩阵进行行变换,化为上三角矩阵。
- 每进行一次行交换,行列式乘以 \(-1\)。
- 每将一行乘以 \(k\),行列式乘以 \(k\)。
- 最终上三角矩阵的行列式等于主对角线元素的乘积。
代码实现(求整数行列式,注意模意义下):
ll det(vector<vector<ll>> a, int n) // a 为 n×n 矩阵
{
ll res=1;
for(int i=0;i<n;i++)
{
if(a[i][i]==0) // 需要换行
{
int j=i+1;
while(j<n&&a[j][i]==0) j++;
if(j==n) return 0; // 秩不满
swap(a[i],a[j]);
res=-res; // 换行变号
}
res=res*a[i][i]%MOD;
ll inv=qpow(a[i][i],MOD-2); // 求逆元,用于消去
for(int j=i+1;j<n;j++)
{
ll factor=a[j][i]*inv%MOD;
for(int k=i;k<n;k++)
a[j][k]=(a[j][k]-factor*a[i][k]%MOD+MOD)%MOD;
}
}
return (res+MOD)%MOD;
}
注意:若模数不是质数,则需用辗转相除法(欧几里得消元)避免除法。
2.4 例题:洛谷 P7112 【模板】行列式求值
直接应用上述算法即可。
三、矩阵的逆
3.1 定义
对于 \(n\) 阶方阵 \(A\),若存在矩阵 \(B\) 使得 \(AB=BA=I\),则称 \(A\) 可逆,\(B\) 称为 \(A\) 的逆矩阵,记作 \(A^{-1}\)。
性质:
- 若 \(A\) 可逆,则 \(A^{-1}\) 唯一。
- \((A^{-1})^{-1}=A\)。
- \((AB)^{-1}=B^{-1}A^{-1}\)。
- \((A^T)^{-1}=(A^{-1})^T\)。
- \(\det(A^{-1})=\frac{1}{\det(A)}\)。
可逆的充要条件:\(\det(A)\neq0\)(在数域上)。
3.2 求逆矩阵的方法:高斯-约旦消元
将 \(A\) 与单位矩阵 \(I\) 并排放置,形成 \([A|I]\)。通过行变换将左边化为 \(I\),右边即为 \(A^{-1}\)。步骤与高斯消元类似,但需同时处理右侧。
代码实现(模质数下):
vector<vector<ll>> inverse(vector<vector<ll>> a, int n)
{
vector<vector<ll>> res(n,vector<ll>(n,0));
for(int i=0;i<n;i++) res[i][i]=1;
for(int i=0;i<n;i++)
{
if(a[i][i]==0)
{
int j=i+1;
while(j<n&&a[j][i]==0) j++;
if(j==n) return {}; // 不可逆
swap(a[i],a[j]);
swap(res[i],res[j]);
}
ll inv=qpow(a[i][i],MOD-2);
for(int j=0;j<n;j++)
{
a[i][j]=a[i][j]*inv%MOD;
res[i][j]=res[i][j]*inv%MOD;
}
for(int j=0;j<n;j++)
{
if(j==i) continue;
ll factor=a[j][i];
for(int k=0;k<n;k++)
{
a[j][k]=(a[j][k]-factor*a[i][k]%MOD+MOD)%MOD;
res[j][k]=(res[j][k]-factor*res[i][k]%MOD+MOD)%MOD;
}
}
}
return res;
}
3.3 例题:P4783 【模板】矩阵求逆
四、线性基(向量空间与异或线性基)
4.1 向量空间的基本概念
- 向量:通常表示为 \(n\) 维数组 \((x_1,\dots,x_n)\)。
- 线性组合:\(c_1v_1+\cdots+c_kv_k\)。
- 线性相关:存在一组不全为零的系数使线性组合为零向量。
- 线性无关:否则。
- 基:一组线性无关的向量,且能张成整个空间。空间维数等于基的大小。
4.2 异或线性基(常用技巧)
在 \(\mathbb{F}_2\) 上,向量空间中的加法为异或(XOR)。一组数的异或线性基是一组数,使得每个数都可以唯一表示为基中某些数的异或。通常用于处理最大异或和、第 \(k\) 小异或和等问题。
构造方法(贪心插入):
- 将每个数 \(x\) 从高位到低位扫描,若当前位为 \(1\),检查基中是否已有该位的基元 \(b[i]\):
- 若无,则将 \(x\) 作为该位基元,结束。
- 若有,则 \(x\gets x\oplus b[i]\),继续。
代码:
struct XorBasis
{
ll a[64];
XorBasis(){memset(a,0,sizeof a);}
void insert(ll x)
{
for(int i=60;i>=0;i--)
if(x>>i&1)
{
if(a[i]) x^=a[i];
else {a[i]=x;return;}
}
}
ll query_max()
{
ll res=0;
for(int i=60;i>=0;i--)
if((res^a[i])>res) res^=a[i];
return res;
}
// 还可求第k小,需先重构
};
性质:
- 基中元素个数不超过 \(\lceil\log_2\max\rceil\)。
- 基能表示的所有数恰好是原数集能异或出的所有数。
4.3 线性基与矩阵秩的关系
将每个数看作行向量,构成的矩阵在 \(\mathbb{F}_2\) 下的秩即为线性基的大小。
4.4 例题:P3812 【模板】线性基
int main()
{
int n;
cin>>n;
XorBasis basis;
for(int i=0;i<n;i++)
{
ll x;
cin>>x;
basis.insert(x);
}
cout<<basis.query_max()<<'\n';
return 0;
}
五、高斯消元法
5.1 解线性方程组
考虑 \(n\) 个方程 \(n\) 个未知数的线性方程组:
写成矩阵形式 \(Ax=b\)。
高斯消元步骤:
- 构造增广矩阵 \((A|b)\)。
- 逐列处理:
- 选取主元(当前列绝对值最大的行,避免精度问题)。
- 将主元行交换到当前行。
- 用主元行消去其他行的当前列。
- 最后得到上三角矩阵,回代求解。
代码(浮点版):
const double eps=1e-8;
vector<double> gauss(vector<vector<double>> a,vector<double> b,int n)
{
for(int i=0;i<n;i++) a[i].pb(b[i]); // 增广矩阵
for(int i=0;i<n;i++)
{
int mx=i;
for(int j=i+1;j<n;j++)
if(fabs(a[j][i])>fabs(a[mx][i])) mx=j;
if(fabs(a[mx][i])<eps) return {}; // 无解或无穷解
swap(a[i],a[mx]);
double tmp=a[i][i];
for(int j=i;j<=n;j++) a[i][j]/=tmp; // 主元化为1
for(int j=0;j<n;j++)
if(j!=i)
{
double rate=a[j][i];
for(int k=i;k<=n;k++)
a[j][k]-=rate*a[i][k];
}
}
vector<double> res(n);
for(int i=0;i<n;i++) res[i]=a[i][n];
return res;
}
整数解:若方程系数和未知数均为整数,且要求整数解,则需使用扩展欧几里得或辗转相除法消元(避免小数)。
5.2 求矩阵的秩
同样通过高斯消元,非零行的个数即为秩。
5.3 例题:P3389 【模板】高斯消元法
六、矩阵快速幂
6.1 线性递推与矩阵乘法
对于线性递推,例如斐波那契数列 \(F_n=F_{n-1}+F_{n-2}\),可以写成矩阵形式:
于是
6.2 矩阵快速幂算法
与整数快速幂类似,只需重载矩阵乘法。复杂度 \(O(n^3\log k)\)。
代码:
Mat mat_pow(Mat a,ll b)
{
Mat res(a.n,a.n);
for(int i=0;i<a.n;i++) res.a[i][i]=1;
while(b)
{
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
6.3 应用:求斐波那契第 \(n\) 项
int main()
{
ll n;
cin>>n;
if(n<=1) {cout<<n<<'\n';return 0;}
Mat trans(2,2);
trans.a[0][0]=1; trans.a[0][1]=1;
trans.a[1][0]=1; trans.a[1][1]=0;
Mat res=mat_pow(trans,n-1);
ll ans=(res.a[0][0]*1+res.a[0][1]*0)%MOD; // 假设 F1=1,F0=0
cout<<ans<<'\n';
return 0;
}
6.4 例题:P1962 斐波那契数列(矩阵加速)
七、矩阵树定理
7.1 定理陈述
对于一个无向图 \(G\)(允许重边,无自环),定义其拉普拉斯矩阵 \(L\):
- \(L_{ii}=\deg(i)\)(顶点 \(i\) 的度数)
- \(L_{ij}=-e_{ij}\),其中 \(e_{ij}\) 为 \(i,j\) 之间的边数(\(i\neq j\))
矩阵树定理:图 \(G\) 的生成树个数等于 \(L\) 的任意一个 \(n-1\) 阶主子式的行列式(即去掉第 \(k\) 行第 \(k\) 列后的行列式)。
7.2 证明思路
利用 Kirchhoff 定理,通过关联矩阵的性质和 Cauchy-Binet 公式可证,此处略。
7.3 有向图的情况
- 对于有向图,定义入度拉普拉斯矩阵 \(L^{\text{in}}\)(出度类似)。以某个根节点 \(r\) 为根的外向树个数等于 \(L^{\text{in}}\) 去掉第 \(r\) 行 \(r\) 列的行列式。
7.4 例题:P4111 [HEOI2015] 小 Z 的房间
题意:网格图中有一些障碍,求生成树个数。
代码要点:
- 给每个非障碍格子编号。
- 构建拉普拉斯矩阵,注意障碍点不参与。
- 计算行列式(模质数)。
int id[10][10]; // 编号
int main()
{
int n,m;
cin>>n>>m;
vector<string> s(n);
for(int i=0;i<n;i++) cin>>s[i];
int tot=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(s[i][j]=='.') id[i][j]=++tot;
if(!tot) {cout<<0<<'\n';return 0;}
Mat L(tot,tot);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(s[i][j]=='.')
{
int u=id[i][j];
if(i+1<n&&s[i+1][j]=='.')
{
int v=id[i+1][j];
L.a[u-1][u-1]++; L.a[v-1][v-1]++; // 度数增加
L.a[u-1][v-1]--; L.a[v-1][u-1]--;
}
if(j+1<m&&s[i][j+1]=='.')
{
int v=id[i][j+1];
L.a[u-1][u-1]++; L.a[v-1][v-1]++;
L.a[u-1][v-1]--; L.a[v-1][u-1]--;
}
}
// 取去掉最后一行最后一列的行列式
Mat K(tot-1,tot-1);
for(int i=0;i<tot-1;i++)
for(int j=0;j<tot-1;j++)
K.a[i][j]=L.a[i][j];
ll ans=det(K,tot-1);
cout<<ans<<'\n';
return 0;
}
八、线性递推与特征多项式
8.1 常系数线性递推
设递推式为 \(a_n=\sum_{i=1}^k c_i a_{n-i}\)(\(n\ge k\)),则可以用矩阵快速幂 \(O(k^3\log n)\) 求解。对于 \(k\) 较大时,可用特征多项式优化到 \(O(k^2\log n)\)。
8.2 特征多项式与 Cayley-Hamilton 定理
对于 \(k\times k\) 矩阵 \(M\),其特征多项式为 \(p(\lambda)=\det(\lambda I-M)\)。Cayley-Hamilton 定理指出 \(p(M)=0\)。因此递推序列满足 \(p(M)\) 的线性关系,可利用多项式取模加速幂运算。
8.3 常用方法:Berlekamp-Massey 算法
给定序列的前若干项,可以求出最短线性递推式。这常用于已知数列找规律。
例题:洛谷 P5487 【模板】Berlekamp-Massey
(由于篇幅,BM 算法实现较复杂,此处不展开)
九、其他重要概念
9.1 矩阵的秩与线性方程组解的结构
- 齐次方程组 \(Ax=0\) 有非零解当且仅当 \(\operatorname{rank}(A)<n\)。
- 非齐次方程组 \(Ax=b\) 有解当且仅当 \(\operatorname{rank}(A)=\operatorname{rank}(A|b)\)。
- 解空间维数 \(=n-\operatorname{rank}(A)\)。
9.2 矩阵的迹
迹 \(\operatorname{tr}(A)\) 是主对角线元素之和,等于所有特征值之和。
9.3 特征值与特征向量
- 若 \(Av=\lambda v\),则 \(\lambda\) 为特征值,\(v\) 为对应特征向量。
- 可通过求解 \((\lambda I-A)v=0\) 得到。
9.4 相似对角化
若 \(A\) 有 \(n\) 个线性无关的特征向量,则 \(A=P^{-1}\Lambda P\),其中 \(\Lambda\) 为对角矩阵。

浙公网安备 33010602011771号