数学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=\begin{pmatrix} a_{11} & a_{12} & \cdots & a_{1n}\\ a_{21} & a_{22} & \cdots & a_{2n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{m1} & a_{m2} & \cdots & a_{mn} \end{pmatrix}\]

也可简记为 \(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\) 矩阵,其中

\[c_{ij}=\sum_{k=1}^n a_{ik}b_{kj}. \]

性质

  • 结合律:\((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|\) 是一个标量,定义为

\[\det(A)=\sum_{\sigma\in S_n}\operatorname{sgn}(\sigma)\prod_{i=1}^n a_{i,\sigma(i)}. \]

其中 \(S_n\) 是所有 \(n\) 元排列的集合,\(\operatorname{sgn}(\sigma)\) 是排列的符号(偶排列为 \(+1\),奇排列为 \(-1\))。

2.2 行列式的基本性质

  1. 单位矩阵\(\det(I)=1\)
  2. 转置不变\(\det(A^T)=\det(A)\)
  3. 交换两行:行列式变号。
  4. 某行乘以 \(k\):行列式乘以 \(k\)
  5. 某行加上另一行的倍数:行列式不变。
  6. 行列式按行展开(拉普拉斯展开)\(\det(A)=\sum_{j=1}^n (-1)^{i+j}a_{ij}M_{ij}\),其中 \(M_{ij}\) 是去掉第 \(i\) 行第 \(j\) 列后的子式。
  7. 乘积定理\(\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\) 个未知数的线性方程组:

\[\begin{cases} a_{11}x_1+a_{12}x_2+\cdots+a_{1n}x_n=b_1\\ a_{21}x_1+a_{22}x_2+\cdots+a_{2n}x_n=b_2\\ \cdots\\ a_{n1}x_1+a_{n2}x_2+\cdots+a_{nn}x_n=b_n \end{cases}\]

写成矩阵形式 \(Ax=b\)

高斯消元步骤

  1. 构造增广矩阵 \((A|b)\)
  2. 逐列处理:
    • 选取主元(当前列绝对值最大的行,避免精度问题)。
    • 将主元行交换到当前行。
    • 用主元行消去其他行的当前列。
  3. 最后得到上三角矩阵,回代求解。

代码(浮点版):

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}\),可以写成矩阵形式:

\[\begin{pmatrix}F_n\\F_{n-1}\end{pmatrix}=\begin{pmatrix}1&1\\1&0\end{pmatrix}\begin{pmatrix}F_{n-1}\\F_{n-2}\end{pmatrix}. \]

于是

\[\begin{pmatrix}F_n\\F_{n-1}\end{pmatrix}=M^{n-1}\begin{pmatrix}F_1\\F_0\end{pmatrix}. \]

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\) 为对角矩阵。

posted @ 2026-03-08 19:28  Augustus_Deception  阅读(3)  评论(0)    收藏  举报