线代,从入门到入门

前置知识:向量,矩阵的基本运算。

初等变换与标准型矩阵

初等变换有三种。

  1. 倍乘,即将矩阵某一行/列乘上 \(k\)
  2. 倍加,即将矩阵某一行/列的 \(k\) 倍加到另一行/列上
  3. 对换,即交换某两行/列

这三种变换都等价于给原矩阵乘上一个与操作对应的初等矩阵。

标准型矩阵:有一个单位阵作为子矩阵位于左上角,其余元素为 \(0\) 的矩阵。

有了上述三种初等变换,我们可以将任意一个矩阵转化成其对应的标准型矩阵。

具体来说,我们先使用倍乘令第一行的第一个元素为 \(1\),如果做不到我们就对换一个能做到的过来。然后我们使用倍加,把 \(2\sim n\) 行都减去第一行乘上该行的第一个元素,就可以让第一列只有第一行有值。然后对剩下的行做同样的操作即可。

需要枚举 \(n\) 行,每行需要初等变换 \(O(n)\) 次,初等变换一次 \(O(n)\),时间复杂度 \(O(n^3)\)

高斯消元

我们给 \(n\times n\) 的方阵加一列,表示每个方程的常数项。然后类似标准型的转化时顺便操作常数项(这里我们不换列,只是要让每行每列至多一个元素,不一定是标准阵),最终得到的就是左边每行一个 \(1\),右边一列常数项。这代表着,矩阵变成了 \(x_i=c\) 的形式,我们解完了。

但是这个不是单位阵,可能存在某些行没有 \(1\) 的情况。此时我们看该行常数项,若它是 \(0\) 那就说明这一行少的那个未知数可以取任意值( \(0x=0\) ),否则就说明这个方程无解 ( \(0x=1\) 之类的 )。

示例代码
//https://www.luogu.com.cn/problem/P2455
#include<cstdio>
#include<algorithm>
using namespace std;
const long double eps=1e-12;
const int mn=105;
long double a[mn][mn];
int n;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n+1;j++)
            scanf("%Lf",&a[i][j]);
    for(int i=1;i<=n;i++)
    {
        if(abs(a[i][i])<=eps)//i位置上为0,怎么倍乘都没用
        {
            bool flag=1;
            for(int j=1;j<=n;j++)//找到第一个 i 位置上不为 0 的
                // j>i 表示还没有选过, abs(a[j][j])<=eps 意味着方程组不存在 j,这样的第 j 列也没被选过
                /*
                不加 || abs(a[j][j])<=eps 的hack:
                in:
                0 2 3
                0 0 0
                out:
                0
                */
                if((j>i || abs(a[j][j])<=eps) && abs(a[j][i])>eps)
                {
                    flag=0;
                    for(int k=i;k<=n+1;k++)swap(a[i][k],a[j][k]);
                    break;
                }
            if(flag)continue;//没有i,跳过
        }
        for(int j=n+1;j>=i;j--)a[i][j]/=a[i][i];//倍乘
        for(int j=1;j<=n;j++)//倍加
        {
            if(j==i)continue;
            for(int k=n+1;k>=i;k--)a[j][k]-=a[j][i]*a[i][k];
        }
    }
    bool flag=0;// 有没有无穷多解
    for(int i=1;i<=n;i++)
        if(abs(a[i][i])<=eps)
        {
            if(abs(a[i][n+1])>eps)return printf("-1\n"),0;
            flag=1;
        }
    if(flag)return printf("0\n"),0;
    for(int i=1;i<=n;i++)printf("x%d=%.2Lf\n",i,a[i][n+1]);
}

稍加修改即可通过另一个板子。

高消还可以求解异或方程组。原理相同,但是可以使用 bitset 从而将时间复杂度降低到 \(O(\frac{n^3}\omega)\)。例题是外星千足虫

矩阵求逆

对于矩阵 \(A\),若存在一个矩阵 \(B\) 使得 \(AB=I\),则我们称 \(B\)\(A\) 的逆矩阵,记作 \(B=A^{-1}\)

上文 我们提到了,初等变换等价于给原矩阵乘上一个与操作对应的初等矩阵。假设这个矩阵能够求逆,则他最后化得的标准矩阵就是单位矩阵 \(I\)

设我们将该矩阵化为标准矩阵的操作序列所对应的初等矩阵序列为 \(\{X_i\}\),则我们很明显有 \(A\prod X_i=AA^{-1}=I\) ,由矩阵乘法结合律我们有 \(\prod X_i=A^{-1}\)。在实现上,我们通常拿一个单位阵和原矩阵拼在一起,然后一起进行操作。原矩阵变为单位阵时,单位阵就变为了其逆矩阵。

线性基

  • 线性相关: 对于一组向量 \(v_i\),若存在一个不全为 \(0\) 的实数序列 \(a\) 使得 \(\sum a_iv_i=0\) ,则称这组向量线性相关。

  • 线性基(基底): 从一个向量集合中选出的最大的线性无关子集。

  • 向量空间: 能用线性基拼出来的所有向量的集合。

性质:如果一组 \(k\) 维向量线性基的大小恰为 \(k\),则这组向量可以表示出整个 \(k\) 维空间。

异或空间线性基

给你一组二进制下长为 \(w\) 的数,问你能异或出来哪些值?

这个可以看作是在 \(w\) 维异或空间内的一个向量集,由前面提到的性质,\(w\) 个数就可以构成一组基底。

求解这个线性基我们可以使用高斯消元。写了上面的高消解异或方程组的童鞋应该能写出这样的代码。

for(int i=1;i<=n;i++)
{
    if(!a[i][i])
    {
        int t=m+1;
        for(int j=i+1;j<=m;j++)if(a[j][i] && id[j]<id[t])t=j;
        if(t>m)return printf("Cannot Determine\n"),0;
        swap(a[t],a[i]);
        swap(id[t],id[i]);
    }
    ans=max(ans,id[i]);
    for(int j=1;j<=m;j++)if(j!=i && a[j][i])a[j]^=a[i];
}

我们把上述过程更改为,每次拿出一个数,如果他这一维有值,就看有没有这一维对应的基底,如果有,那我们把这个数异或上基底,然后再去看下一维。感知一下和上面那个是等价的。然后我们就水过了模板

#include<cstdio>
#define ll long long
using namespace std;
int n;
ll d[55];
int main()
{
    ll x;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&x);
        for(int i=49;i>=0;i--)
            if(x&(1LL<<i))
            {
                if(!d[i]){d[i]=x;break;}
                x^=d[i];
            }
    }
    x=0;
    for(int i=49;i>=0;i--)if(d[i] && !(x&(1LL<<i)))x^=d[i];
    printf("%lld\n",x);
}

容易发现这个复杂度是 \(nlogV\) 的,其中 \(V\) 是值域。

查询最大值,只需判断当前位上有无基底,且异或之后是否变大。最小值就是最小的基底。如果要查第 \(k\) 大,那我们就把高消做完,即让每个基底的 popcount \(=1\),然后用 \(k\) 的第 \(i\) 位是 \(0/1\) 来决定是否异或上第 \(i\) 个基底。

合并两个线性基,只需暴力把一个线性基里的元素插到另一个里。复杂度 \(O(\log^2 n)\)

posted @ 2025-08-16 10:08  ikusiad  阅读(33)  评论(0)    收藏  举报