线性基

线性基

​ 线性基是什么?王总说:“OI 中所说的线性基,其实是位线性空间下的在线化矩阵求秩。”

​ enmmm.......差不多是这个样子,但是想要通过这样来理解线性基还要学好多好多前置知识,先说一下个人的理解吧。

​ 首先,你要知道线性基在干什么:线性基在线的维护一个小集合 \(T\) ,在集合内选几个数异或起来,能够表示所有当前大集合 \(S\) 里的二进制数,想要理解的更深一点的解释可以见下面。

​ 然后由于异或的自反性,也就是无论是加入还是删除掉 \(x\) 的贡献,我们都可以通过异或来达到,这样的话我们可以设一个数\(y=x_{a_1}\oplus x_{a_2}...\oplus x_{a_k}\),这样我们可以在 \(S\) 中删去 \(x_{a_1}\),加入 \(y\),这样,如果我们想表示\(x_{a_1}\),就可以通过\(y\oplus x_{a_2}...\oplus x_{a_k}\)来表示。

​ 那么我们就可以重新组合这些 \(x\) ,这里给出线性基的构造方法:将当前的 \(y_i\) ,也就是通过\(x\)构造出来的数从上到下排好之后就是一个上三角,比如:

\[第1行\;\;\; 1\;0\;1\;1\;0\\ 第2行\;\;\; 0\;1\;0\;1\;1\\ 第3行\;\;\; 0\;0\;1\;0\;0\\ 第4行\;\;\; 0\;0\;0\;1\;1\\ 第5行\;\;\; 0\;0\;0\;0\;1\\ \]

​ 可以看到对于\(i\)行,只有\(i\sim n\)位才有值。如果我们想构造出 \(x\)(将 \(x\) 异或为 \(0\)) ,这样的话我们从高位往低位遍历,如果第 \(i\) 位为1,那么我们就将\(x\leftarrow x\oplus y_i\),只要这个矩阵是满的,那么对于任意一个\(x\)我们都可以构造出来,而如果矩阵有一行没有值,如:

\[第1行\;\;\; 1\;0\;1\;1\;0\\ 第2行\;\;\; 0\;0\;0\;0\;0\\ 第3行\;\;\; 0\;0\;1\;0\;0\\ 第4行\;\;\; 0\;0\;0\;1\;1\\ 第5行\;\;\; 0\;0\;0\;0\;0\\ \]

​ 这样的矩阵,我们除了第2位和第5位无法构造,其他的几位都可以构造了,方法还是向上面提到的,如果第 \(i\) 位为1,那么我们就将\(x\leftarrow x\oplus y_i\)

​ 现在我们考虑维护这个矩阵,实际上我们维护的是一个数组,对于数组的每一位 \(i\) ,将 \(a_i\) 转化成二进制就是这个矩阵的一行。

​ 我们想往集合 \(S\) 中加入 \(x\) ,前面提到过,\(y_i\)其实是由\(x\)构造出来的,既然如此我们也就尝试用\(x\)构造出\(y\)。依然是从高位往低位遍历,如果\(x\)\(i\) 位为 \(0\) 就跳过,否则看\(y_i\)是否为 \(0\) ,为 \(0\)\(y_i\leftarrow x\),否则\(x\leftarrow x\oplus y_i\)。代码出乎意料的短:

void insert(ll x){
	for(int i=m;i>=0;--i){
		if(!(x>>i))continue;
		if(!y[i]){
			y[i]=x;break;
		}
		x^=y[i];
	}
}

​ 这样建好了之后,我们就可以将这\(y_i\)就当做之前的\(S\),为什么?因为在 \(x\) 插入的过程中,\(x\)不会被插入 \(y\) 中当且仅当\(y_i\)可以构造出 \(x\) ,也就表示这个数已经可以被表示了。

​ 之后的运用就灵活很多了,你就把这些\(y_i\)当做\(S\)就行了。

应用:

  • P3812 【模板】线性基

    给定 \(n\) 个整数,求在这些数中选取任意个,使得他们的异或和最大。

    从高到低,贪心选择,如果当前 \(ans\)\(i\) 位为\(0\),那么就\(ans\leftarrow ans\oplus y_i\)

  • P4839 P哥的桶

    有m个集合\(S_i\),多次操作:

    • \(S_i\)加入一个数\(x\)
    • \(T=\bigcup_{i=l}^r S_i\),并在\(T\)中选任意个数,使他们异或和最大。

    同样和上面的操作一样,但是这里有区间操作,考虑线段树去维护区间内 \(S\) 交的的线性基。

    但是怎么合并呢?注意到我们可以就把当前 \(y_i\) 当做 \(S\) ,那么合并的时候就暴力将右区间有效的 \(y_i\) 全部暴力插入左区间的线性基,单次合并线性基的复杂度是\(O(k^2)\)的。

上面的就是线性基的基本操作,查询、合并,之后的就是对这些操作的运用。

  • P3292 [SCOI2016]幸运数字

    一棵树有n个节点,每个节点有个数 \(a_i\) ,多次询问,每次询问给一条路径,设\(T=\bigcup _{i\in \text{path(u,v)}}\{a_i\}\),并在\(T\)中选任意个数,使他们异或和最大。

    当然可以树剖做,但是这样是\(O(q\log k\log^2 n)\),可以考虑点分治,这样对于一次询问我们只需要合并一次线性基,这样的复杂度就是\(O(q\log n\log k)\)

  • CF938G Shortest Path Queries

    给出一个连通带权无向图,边有边权,要求支持 \(q\) 个操作:

    \(1 \;x \;y \;d\) 在原图中加入一条 \(x\)\(y\) 权值为\(d\) 的边。

    $2 ;x ;y $ 把图中 \(x\)\(y\) 的边删掉。

    $3 ;x ;y $ 表示询问 \(x\)\(y\) 的异或最短路。

    先考虑没有修改的情况,因为异或的自反性,我只要从\(x\rightarrow y\),之后\(y\rightarrow x\),发现带来的贡献为 \(0\) ,所以我们可以找到一个环,然后找环上的一个点 \(y\) ,从当前的点 \(x\)\(y\) ,之后再环上转一圈,记录上环的贡献,再回到 \(x\)

    所以我们可以找到所有图里面的上的环,加入一个线性基,之后随便找一条\(x\rightarrow y\)的路径就可以了,但是环可以有很多,我们不能一个个找。

    那么我们建立一棵生成树,之后对于一条非树边,就对应了一个环。而包含多条非树边的环一定会被多条这样的环给表示出来。

    现在考虑带修改的情况,注意线性基加入简单但是删除困难,所以我们可以使用线段树分治,同时用带权并查集维护生成树。

关于线性基的本质分析

​ 先给出几个概念(以向量为例):

  • 向量空间 \(S\) :向量的集合。
  • 初等变换:几个向量加权(数乘)之后加在一起得到向量。
  • 线性无关:没有向量可用有限个其他向量的线性组合所表示。
  • 基/秩:若向量集合 \(B\subseteq S\) ,并且 \(B\) 中的元素线性无关,则称 \(B\) 是一组基。

​ 然后你要知道初等变换不影响矩阵的秩,然后就可以通过高斯消元求解。其实仔细想一想,通过其他向量构造出当前这个向量就是解一个方程组,而高斯消元表面上也就是解方程组的算法。

​ 而二进制情况下,高斯消元的处理过程就特别简单了,通过在线的维护高斯消元的上三角,我们从上到下消元就行了。

posted @ 2022-02-17 15:13  qwq_123  阅读(79)  评论(0编辑  收藏  举报