线性基学习笔记
\(\text{线性基学习笔记}\)
定义
OI 中线性基一般指异或空间线性基。具体地,对于一个数集 \(S\),我们要找到一个集合 \(T\) 使得 \(S\) 内数的异或集合和 \(T\) 内数的异或集合相同,且 \(T\) 的大小最小。
构建
一般使用贪心法构造线性基。设线性基数组为 \(p\),当前插入的数为 \(x\),那么我们每位从高到低枚举 \(p\),如果当前处在的第 \(i\) 位上 \(x\) 的值为 \(1\),那么 \(x\leftarrow x\oplus p_i\);如果 \(p_i=0\),那么 \(p_i\leftarrow x\)。
这样做的正确性基于保证 \(x\) 可以被表示的前提下尽可能低地扩展了二进制位。这里给出构建的代码:
void insert(int x) {
for (int i = M - 1; ~i; --i)
if ((x >> i) & 1) {
if (!p[i]) {
p[i] = x;
return;
}
x ^= p[i];
}
}
查询
一般的应用是求最大/最小异或和。贪心地,从高往低尽量选择能使答案变大/小的位即可。需要留意的是,求最小异或和时,如果上文插入过程中有 \(x\) 未被插入,那么显然 \(x\) 本身已经可被表示,也就是实际上原数集有异或和位 \(0\) 的子集,特判一下即可。
给出求最大异或和的代码:
int query() {
int ans = 0;
for (int i = M - 1; ~i; --i)
ans = max(ans, ans ^ p[i]);
return ans;
}
合并
就是暴力将一个线性基插到另一个线性基里。代码不放了。
求排名
求去重的排名
略微思考一下不难发现对于 \(p_i\),我们实际上只关心其有没有值。如果有,那么意味着 \(i\) 这一位可以选择是否异或来选择成为 \(0/1\),反之选择是固定的。那么我们从小往大枚举每一位,假使当前枚举到第 \(k\) 个有值的线性基,如果此时 \(x\) 同样有值,排名会加上 \(2^{k-1}\) 来表示这一位为 \(0\) 时的情形,反之不予操作即可。
代码:
int nrk(int x) {
int ans = 0, sum = 1;
for (int i = 0; i < M; i++) {
if (!p[i]) continue;
if ((x >> i) & 1) ans += sum;
sum <<= 1;
}
return ans;
}
求不去重的排名
考虑在线性基中有 \(cnt\) 个数插入失败,这意味着这 \(cnt\) 个数任意组合选择都能和线性基内的一些数异或起来得到 \(0\)。那么任选的方案有 \(2^{cnt}\) 种,于是答案就是 \((rnk-1)\times2^{cnt}+1\)。
图上异或问题
对于一些图上异或问题,可以采用线性基解决。
例:求无向图上两点间的异或最长路。
能够发现的性质是任意一条路径可以由一条 \(s\to t\) 的链和若干简单环组合而成。于是把环挂到线性基上求异或最大值即可。

浙公网安备 33010602011771号