异或线性基
前言
本篇博客中,使用 \(|S|\) 表示集合 \(S\) 的大小,\(\oplus\) 表示异或运算,\(\bigoplus\limits_{i=l}^{r}a_i\) 表示 \(a_l \oplus a_{l + 1} \oplus a_{l + 2} \oplus ... \oplus a_r\)。
概念
线性基
称线性空间 \(V\) 的一个极大线性无关组为 \(V\) 的一组 Hamel 基 或 线性基,简称 基。
规定线性空间 \(\{\theta\}\) 的基为空集。
另外,将 \(V\) 的 维数 记作 \(\dim V\), 定义为基的元素个数。
简单来说,如果能够通过一个向量集合 \(V_1\) 内的向量的加减乘除表示出另一个向量集合 \(V_2\) 中的所有向量,则称大小最小的 \(V_1\) 为 \(V_2\) 的线性基。
但此处我们要讲解的线性基不是这个线性基,但他的性质和这个线性基相似。
异或线性基
异或线性基才是本篇博客涉及的线性基。
一个二进制数集 \(S\) 的异或线性基是最小的二进制数集 \(S_0\),满足\(S\) 中任意一个二进制数都可以表示成若干个 \(S_0\) 中二进制数的异或和(不选异或和为 \(0\))。
性质
显而易见,数集 \(S\) 的线性基 \(S_0\) 满足以下性质:
-
对于任意若干个 \(S\) 中的数的异或和,都可以表示成若干个 \(S_0\) 中的数的异或和。
-
不存在任意 \(k\)(\(k > 0\))个 \(S_0\) 中的数的异或和为 \(0\)。
-
\(S_0\) 中任意不同选择方案选择出的数的异或和都不相同,或者说,总共有 \(2^{|S_0|} - 1\) 个不同的 \(S_0\) 中若干个数的异或和。
操作
一般线性基是动态维护的,我们需要动态维护 \(S\) 集合的线性基 \(S_0\),支持在线插入查询。
插入
插入操作是将一个二进制数 \(k\) 插入 \(S\) 后,更新线性基 \(S_0\)。
若 \(k\) 可以被线性基 \(S_0\) 表示出,那么 \(S\) 插入 \(k\) 之后,\(S_0\) 不会有任何变化。
若 \(k\) 不可以被线性基 \(S_0\) 表示出,则我们需要在 \(S_0\) 中新增一个数 \(k_0\),满足存在一个 \(J \in S_0\),使得
你可能会想到,为什么不直接让 \(k_0 = k\)?
显然可以,但是如果这样插入,判断 \(k\) 是否可以被 \(S_0\) 表示出时则需要更多时间,我们现在需要寻找一个更好的方法使得判断和插入时间复杂度平衡。
我们重新考虑。
如果线性基 \(S_0\) 内有一个数某一位为 \(1\),那么在不考虑其他位的情况下,我们一定可以选择出多个数让他们的异或和在这一位为 \(0\),或者 \(1\)(当 \(|S_0| > 1\) 时)。
对于任意 \(s \in S_0\),由于其二进制下最高位 \(i\)(最高为 \(1\) 的位)之后更高位没有数字,所以我们可以在不改变更高位的情况下,通过选择或者取消选择更改最终异或和的第 \(i\) 位。
对于一个位 \(i\),如果线性基中有且仅有一个最高位为第 \(i\) 位的数,那么我们想要在不更改更高位的情况下修改第 \(i\) 位,只能选择(或者取消选择)这个数。
所以,如果我们让线性基中任意两个数的最高位都不同(这是显然可以做到),我们就可以快速的判断是否可以表示出 \(k\) 了。
于是新增一条线性基的性质:
- 线性基 \(S_0\) 中任意两个数的最高位都不相等。
方法如下:
如果我们可以在 \(S_0\) 中选择出一些数,使得他们的异或和 \(a\),满足 \(a \oplus k = 0\),那么就代表 \(k\) 可以被表示出。
从上往下遍历,假设当前遍历到第 \(i\) 位,当前选择的数异或和为 \(a\):
若 \(k \oplus a\) 第 \(i\) 位为 \(0\),则我们不需要操作。
若 \(k \oplus a\) 第 \(i\) 位为 \(1\),此时需要判断是否存在最高位为第 \(i\) 位的数 \(b\)。
若存在,则选择上 \(b\),即将 \(a\) 更改为 \(a ^ b\)。
若不存在,则代表该位无法在不更改更高位的情况下清零,也就是 \(k\) 无法被 \(S_0\) 中的数表示出。
有了上面判断方法后,我们来思考如何插入。
插入时我们需要满足:线性基中任意两个数的最高位的位置都不同。
判断的过程中,当遍历到第 \(i\) 位时,\(a \oplus k\) 的更高位实际上已经全部为 \(0\) 了。当我们发现不存在最高位为第 \(i\) 位的数的时候,\(a \oplus k\) 恰好满足上面的要求。
直接将当前的 \(a \oplus k\) 丢入 \(S_0\)。
查询
查询操作一般用于快速计算集合 \(S\) 中所有任意若干个数的异或和的 \(k\) 小值。
方便起见,我们将线性基 \(S_0\) 中的数由大到小排序,用 \(a_1, a_2, ..., a_{|s_0|}\) 表示。
对于每个 \(i\),我们设 \(b_i\) 代表 \(a_i\) 最高位位数。
由于线性基的性质一,集合 \(S\) 中任意若干个数的异或和构成的集合等于 \(S_0\) 中任意若干个数的异或和构成的集合。
- 对于任意若干个 \(S\) 中的数的异或和,都可以表示成若干个 \(S_0\) 中的数的异或和。
所以只需要求出 \(S_0\) 中所有任意若干个数的异或和的 \(k\) 小值即是答案。
线性基的性质三可以帮助我们快速计算 \(k\) 小值。
- \(S_0\) 中任意不同选择方案选择出的数的异或和都不相同,或者说,总共有 \(2^{|S_0|} - 1\) 个不同的 \(S_0\) 中若干个数的异或和。
首先考虑整个线性基中的最高位最高的数 \(a_1\),因为其他数都无法更改第 \(b_1\) 位的数字,所以如果选择了 \(a_1\),则代表最终异或和第 \(b_1\) 位为 \(1\)。
因为线性基满足前面的性质,所以选择了 \(a_1\) 和不选择 \(a_1\) 各有 \(2^{|S_0| - 1}\) 个选择,而选择了 \(a_1\) 会使最终异或和更大,即让异或和的排名增加了 \(2^{|S_0| - 1}\) 名。
如果 \(k > 2^{|S_0| - 1}\),我们就选择 \(a_1\),反之不选。
通过这个方法,我们可以从高处往下遍历,求得答案。
更特殊地,如果当前判断是否选择的数为 \(a_i\),已经决定了最终异或和 \(A\) 的第 \(b_i\) 位的更高位,在未决定的位置的排名为 \(k\)。
此时,因为选择 \(a_i\) 只影响 \(A\) 第 \(b_i\) 位之下的位,选择 \(a_j(j>i)\) 不会影响第 \(b_i\) 位。所以最终答案第 \(b_i\) 位被 \(A\) 决定。
此时,如果 \(k > 2^{|S_0 - i|}\),我们就使 \(A ^ a_i\) 第 \(b_i\) 位为 \(1\),反之同理。
从 \(1\) 到 \(|S_0|\) 对所有 \(i\) 按顺序进行如上操作,最终的 \(A\) 就是答案。
浙公网安备 33010602011771号