线性基
面对规劝学会取舍后回答
疲于应对视察 理想被爱拖垮
心跳 正在渐渐同化
直面思绪扯拉 为人生做减法
追求稳妥应是正确作答?
是生活的重压 对未知的惧怕
泪水 在溃塌
——洛天依《冻梦》
线性基
一个 \(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
天依宝宝可爱!
洛谷 P4151
思维难度:\(\color{#FFC116} 黄\) *1500
先考虑图是一棵树的情况,这时候显然答案就是 \(1 \leadsto n\) 的异或和,因为即使在路径中间往外走一条边,也必须原路返回,这样就相当于没有贡献。
然后考虑有环的情况。注意到这是往外走一条边 \(e\) 之后,如果绕一个环走一圈,再由 \(e\) 走回来,那么就会产生这个环的异或和的贡献,而且 \(e\) 不会产生任何贡献!
所以就可以考虑把图上所有环拿出来算下异或和丢到线性基里,然后求 \(1 \leadsto n\) 的异或和与线性基里的元素的异或和的最大值就可以了。
但是稍微注意一下会发现,有时候 \(1 \leadsto n\) 是存在多条路径的,不过这时候上面做法的正确性仍然成立,因为如果有多条路径,那么每两条路径就会构成一个环,一条路径异或上这个环就等价于另一条路径了。
关于求出图上所有的环,其实并不需要真的找到全部环,只需要 dfs 一遍找返祖边即可。为什么是对的呢?可以发现,这样找到的环经过一些边集的「异或」操作,总能得到图上所有的环,可以理解为找到的环集 \(S\) 是全环集 \(U\) 的一组基(但不保证线性无关),再把它们丢到线性基里之后,线性基里的环集 \(S_0\) 就是 \(S\) 的一组基,显然也就是 \(U\) 的一组基了。
天依宝宝可爱!
洛谷 P3292
思维难度:\(\color{#F39C11} 橙\) *1000
直接倍增跳 lca 过程中暴力合并线性基,复杂度 \(O(n \log n \log V) - O(q \log n \log^2 V)\),看起来非常不能过的样子,但是跑得飞快,最大点只跑了 2s 不到。猜测是线性基合并严重跑不满导致的。
天依宝宝可爱!
洛谷 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\),即原序列)的每个子集,所以显然这也是上界。
天依宝宝可爱!
HDU 3949
思维难度:\(\color{#F39C11} 橙\) *900
查询 \(k\) 大板子题。
高斯消元构造的线性基对查询 \(k\) 大很友好,只需要把 \(k\) 二进制拆分,枚举线性基每一位有值的位置,如果 \(k\) 第 \(j\) 为 \(1\) 则把答案异或上 \(a_j\) 即可。
注意线性基位数 bit 不能开 \(64\),最多开 \(63\),如果必须要开 \(64\) 需要用 ull。
天依宝宝可爱!
CF1163E
思维难度:\(\color{#FFC116} 黄\) *1400
CF 题太好玩了,巧妙,思维含量高,不像 CNOI 套路一大堆还很难写。
就像你在学校,抬头是理想(CF),而低头是生活(CNOI)。
学校的晚霞格外好看。
——某群群 u
注意到 \(0\) 很特殊,所以考虑从 \(0\) 入手。显然一个合法的序列一定是长这个样的:
所以序列里的每个数都必须是由若干个 \(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\) 中某些元素异或起来的结果,所以要标记线性基每一位的数在插入前是什么,然后把这些数异或起来。
看起来好像挺对的。
天依宝宝可爱!

浙公网安备 33010602011771号