线性基

面对规劝学会取舍后回答

疲于应对视察 理想被爱拖垮

心跳 正在渐渐同化

直面思绪扯拉 为人生做减法

追求稳妥应是正确作答?

是生活的重压 对未知的惧怕

泪水 在溃塌

——洛天依《冻梦》


线性基

一个 \(O(\log V)\) 处理异或问题的工具,知道下面的两个重要性质就能做很多事情了。

原理不用知道,只要构造的代码背过就可以了。

天依宝宝可爱!


洛谷 P3812

思维难度:\(\color{#FE4C61} 红\) *800

板子题。

线性基就是一个具有一些性质、能求一些异或相关的东西的东西,具体证明不知道,也不需要知道。

性质:

  • 重要性质\(n\) 个元素能异或出来的所有数 都能被 大小为最大二进制位(即 \(\lfloor \log V \rfloor\))的线性基里的元素 异或出来。
  • 重要性质:线性基的不同子集异或出来的值一定不同。
  • 线性基内任意一个子集的异或和不为 \(0\)(所以在一些情况下需要特判。
  • 同一个数集的线性基不唯一,不过元素个数都一样。

支持的操作:

  • \(O(\log V)\) 插入一个数 \(x\)
  • \(O(\log V)\) 求一个数 \(x\) 异或线性基里的元素的最大 / 最小异或和。当 \(x=0\) 时最小异或和可 \(O(1)\) 求。
  • \(O(\log V)\) 判断某个数是否能被异或出来。
  • \(O(\log V)\) 求一个数在线性基能异或出来的所有元素中的排名。
  • \(O(\log V)\) 求异或第 \(k\) 大 / 第 \(k\) 小(仅限于高消构造的线性基,否则需要 \(O(\log^2 V)\) 的重构。
  • \(O(\log^2 V)\) 合并两个线性基。

插入有两种方法。一个是贪心,一个是简化版高消,其中后者构造的线性基对于一些查询操作更加方便,所以只学后者。

当插入 \(x\) 时,考虑从大到小遍历 \(x\)\(1\) 的每个二进制位 \(i\),如果线性基里面第 \(i\) 位里面插入过 \(1\)(即 \(a_i \ne 0\)),就把 \(x\) 异或上 \(a_i\);否则将 \(x\) 插入位置 \(i\),然后消元。先往低位的方向消元,从 \(i-1\)\(0\) 遍历 \(x\)\(1\) 的每个二进制位 \(j\),把 \(x\) 异或上 \(a_j\);再往高位消元,若 \(a_j\) 的第 \(i\) 位为 \(1\),则把 \(a_j\) 异或上 \(x\)

记住过程,代码就好记了。

rpe(i,bit,0) if(x>>i&1)
{
	if(a[i]) x^=a[i];
	else
	{
		rep(j,0,i-1) x>>j&1 && (x^=a[j]);
		rep(j,i+1,bit) a[j]>>i&1 && (a[j]^=x);
		a[i]=x;
		break;
	}
}

构造出来的线性基是个不完整的对角矩阵,满足对角线有 \(1\) 的位置所在列全为 \(0\),否则上方为 \(0\),下方不一定。

例子:

1000000000
0000000000
0010000000
0000000000
0100100000
0000000000
0100001000
0001000100
0001010010
0100000001

submission

天依宝宝可爱!


洛谷 P4151

思维难度:\(\color{#FFC116} 黄\) *1500

先考虑图是一棵树的情况,这时候显然答案就是 \(1 \leadsto n\) 的异或和,因为即使在路径中间往外走一条边,也必须原路返回,这样就相当于没有贡献。

然后考虑有环的情况。注意到这是往外走一条边 \(e\) 之后,如果绕一个环走一圈,再由 \(e\) 走回来,那么就会产生这个环的异或和的贡献,而且 \(e\) 不会产生任何贡献!

所以就可以考虑把图上所有环拿出来算下异或和丢到线性基里,然后求 \(1 \leadsto n\) 的异或和与线性基里的元素的异或和的最大值就可以了。

但是稍微注意一下会发现,有时候 \(1 \leadsto n\) 是存在多条路径的,不过这时候上面做法的正确性仍然成立,因为如果有多条路径,那么每两条路径就会构成一个环,一条路径异或上这个环就等价于另一条路径了。

关于求出图上所有的环,其实并不需要真的找到全部环,只需要 dfs 一遍找返祖边即可。为什么是对的呢?可以发现,这样找到的环经过一些边集的「异或」操作,总能得到图上所有的环,可以理解为找到的环集 \(S\) 是全环集 \(U\) 的一组基(但不保证线性无关),再把它们丢到线性基里之后,线性基里的环集 \(S_0\) 就是 \(S\) 的一组基,显然也就是 \(U\) 的一组基了。

submission

天依宝宝可爱!


洛谷 P3292

思维难度:\(\color{#F39C11} 橙\) *1000

直接倍增跳 lca 过程中暴力合并线性基,复杂度 \(O(n \log n \log V) - O(q \log n \log^2 V)\),看起来非常不能过的样子,但是跑得飞快,最大点只跑了 2s 不到。猜测是线性基合并严重跑不满导致的。

submission

天依宝宝可爱!


洛谷 P4869

思维难度:\(\color{#FFC116} 黄\) *1300

首先线性基查排名是容易的,因为不同子集异或出来的数一定不同,所以查 \(x\) 的排名直接把 \(x\) 二进制拆位,看看线性基里每一个有值的位置 \(i\),在 \(x\) 的第 \(i\) 位是否为 \(1\) 即可。当然前提是要保证 \(x\) 一定可以被异或出来。

但是线性基是去重的,而这题要求的是不去重。所以就要求异或出来的每个数的出现次数。

结论:观察样例可以发现,每个数的出现次数均为 \(2^{未成功插入线性基的元素个数}\)

Proof. 令 \(S\) 为成功插入线性基里的数集,\(T\) 为未成功插入线性基里的数集。那么根据线性基的构造过程,显然 \(T\) 中的任何一个数都可以被 \(S\) 的一个子集 \(S_0\) 异或出来,进而 \(T\) 中的任何一个子集 \(T_1\) 都可以被 \(S\) 的一个子集 \(S_1\) 异或出来。

所以对于线性基中可以异或出来的任意一个数 \(x\),对于任意 \(T_1\),都可以找到对应的 \(S_1\) 使得 \(T_1 \oplus S_1 = 0\),所以 \(x \oplus T_1 \oplus S_1 = x\)。又因为两两互异的 \(T_1\)\(2^{|T|}\) 个,所以 \(x\) 出现次数的下界就是 \(2^{|T|}\)

然后发现,显然不同的 \(x\) 总共有 \(2^{|S|}\) 个(包括 \(0\)),所以按照下界把所有出现次数加起来,共有 \(2^{|S|} \times 2^{|T|} = 2^n\) 个,正好对应了原数集(即 \(S \cup T\),即原序列)的每个子集,所以显然这也是上界。

submission

天依宝宝可爱!


HDU 3949

思维难度:\(\color{#F39C11} 橙\) *900

查询 \(k\) 大板子题。

高斯消元构造的线性基对查询 \(k\) 大很友好,只需要把 \(k\) 二进制拆分,枚举线性基每一位有值的位置,如果 \(k\)\(j\)\(1\) 则把答案异或上 \(a_j\) 即可。

注意线性基位数 bit 不能开 \(64\),最多开 \(63\),如果必须要开 \(64\) 需要用 ull。

submission

天依宝宝可爱!


CF1163E

思维难度:\(\color{#FFC116} 黄\) *1400

CF 题太好玩了,巧妙,思维含量高,不像 CNOI 套路一大堆还很难写。

就像你在学校,抬头是理想(CF),而低头是生活(CNOI)。
学校的晚霞格外好看。
——某群群 u

注意到 \(0\) 很特殊,所以考虑从 \(0\) 入手。显然一个合法的序列一定是长这个样的:

\[\ldots , s_i \oplus s_j , s_i , 0 , s_i' , s_i' \oplus s_j' , \ldots \]

所以序列里的每个数都必须是由若干个 \(s\) 中的元素异或出来的。

因为需要是 \(0 \sim 2^x - 1\) 的排列,所以一个必要条件就是 \(0 \sim 2^x - 1\) 一定能被 \(s\) 的一个子集异或出来,显然等价于线性基的长度为 \(x\) 前缀是都有值的,所以 \(x\) 的最大值就是 \(s\) 的线性基的最大前缀。

考虑构造,注意到要求的是相邻两个元素的异或和均为 \(s\) 中的元素,所以不妨标记每个元素是 \(s\) 中哪些元素异或出来的,记为一个 \(01\) 序列,于是就需要求一个 \(01\) 序列使得它是一个 \(0 \sim 2^x - 1\) 的排列且相邻两位有且仅有一位不同,可以发现就是格雷码,参照 P5657 的构造方式即可。

注意到我们求出来的是编号序列,需要转化为 \(s\) 中某些元素异或起来的结果,所以要标记线性基每一位的数在插入前是什么,然后把这些数异或起来。

看起来好像挺对的。

submission

天依宝宝可爱!

posted @ 2025-08-19 09:55  little__bug  阅读(10)  评论(0)    收藏  举报