数学/数论专题-学习笔记:线性基

1. 前言

线性基,是线性代数中的一个板块,专门处理异或问题。

注意作者是个 OIer,因此并不会涉及(或者是极少的)线性代数知识。

注意本文所讲线性基跟向量无关。

前置知识:二进制,位运算。

2. 详解

2.1 定义与性质

线性基的定义如下:

给出一个数列 \(a_1,a_2,...,a_n\),若一个序列 \(d_1,d_2,...,d_k\) 满足以下性质:

  • 所有原数列中的元素能够异或出来的值 \(d\) 中的数也能异或出来。
  • 满足性质 1 的前提下,\(d\) 中的数不能异或出 0。
  • 满足性质 2 的前提下,\(d\) 中的数最少。

那么就称 \(d\) 是原序列 \(a\) 的一组线性基。

注意上述性质中的异或指的是从数列中选出一些数一起异或。

实际上任意数列 \(a\) 都有至少一组线性基

2.2 构造线性基

这个方法需要用到一个性质:\(a \oplus b=c \Leftrightarrow a \oplus c=b\)

首先考虑将所有 \(a_i\) 转成二进制,然后从二进制最高位开始扫,对于第 \(x\) 位,如果 \(d_x\) 不存在,那么 \(d_x=a_i\),否则 \(a_i \gets a_i \oplus d_x\),一个一个插入即可。

Code:

void add(LL x)
{
    for (int i = 50; i >= 0; --i)
    {
        if (x & (1ll << i))
        {
            if (d[i] & x) x ^= d[i];
            else { d[i] = x; break ; }
        }
    }
}

那么这份代码如何满足上述 3 个性质呢?


性质 1:原序列能够异或出来的数线性基也能够异或出来。

性质 2:线性基内的数不能异或出 0。

这两条性质都是根据 \(a \oplus b=c \Leftrightarrow a \oplus c=b\) 来证明的。

由于 \(x \oplus d_i = d_j \Leftrightarrow d_i \oplus d_j = x\),因此性质 1 得证。

性质 2 采用反证法:假设 \(d_i \oplus d_j \oplus d_k=0\)\(d_k\) 最晚插入。

这里只讨论 3 个数的理由是实际上你可以将 \(d_i\) 看成多个数异或。

上式等价于 \(d_i \oplus d_j= d_k\)

于是根据代码,\(d_k\) 不应该被插入线性基,矛盾。


性质 3 的证明:

显然对于所有存在的 \(d_i\) 而言,第 \(i\) 位二进制位一定是 1。

既然如此,如果删去任何一个数,都会导致有一位二进制位空缺,因此这些数是必要的,同时又是最少的。

3. 应用

3.1 最大值问题

该问题的详述描述如下:给出数列 \(\{a_i\}\),从中选一些数使其异或结果最大。

根据线性基的性质 1,我们可以构造线性基。

构造之后直接暴力按照 \(d_i\) 从大到小枚举,然后看一下跟答案异或能否使答案变大,能就异或。

实际上就是一个贪心的思想。

为什么这样贪心是对的呢?

如果第 \(i+1\) 位通过异或变成了 1,即使 \(1-i\) 位通过异或都变成了 0,答案也一定是增大的。

因此贪心是正确的。

3.2 最小值问题

这个问题有两类:

第一类问题:问线性基能异或出的最小值。

这个直接就是最小的 \(d_i\),因为最小的 \(d_i\) 无论异或谁都会变大。

第二类问题:问原序列能异或出的最小值。

这个还需要看一下有没有元素插入失败,如果有就是 0,否则还是最小的 \(d_i\)

3.3 第 k 小问题

该问题的详细描述如下:给出序列 \(\{a_i\}\),取出任意数进行异或,问能异或出的所有值的第 \(k\) 小。

首先我们需要对线性基做一个处理。

对于每一个 \(d_i\),如果 \(d_i\) 二进制表示第 \(j\) 位为 1,那么 \(d_i \gets d_{j-1} \oplus d_i\)

然后对 \(k\) 做二进制拆分,如果第 \(i\) 位为 1,那么答案异或上 \(d_i\)

这样做的正确性就是考虑到 \(d_i\) 实质作用是提供最高位的 1,因此只要所有 1 能够跟 \(k\) 对应答案就是第 \(k\) 小。

实际上转换后的线性基还是线性基。

3.4 异或和问题

详细描述:给出序列 \(\{a_n\}\),选若干个数异或,问可能的结果的和。

首先得出线性基,设大小为 \(s\),枚举每一个二进制位 \(d\),如果存在一个线性基中的数,该位为 1,那么这位的贡献是 \(2^d \times 2^{s-1}\),所有贡献加起来即可。

正确性证明:\(2^d\) 是这一位的值,\(2^{s-1}\) 是别的位能异或出的结果种数,保证结果种数互不相同是因为保证个数最小,此时就保证了不会出现两组不同的数,其异或值相同。

4. 总结

线性基的三大性质:

  • 所有原数列中的元素能够异或出来的值线性基中的数也能异或出来。
  • 线性基中的数不能异或出 0。
  • 线性基中的数最少。

构造方式:首先考虑将所有 \(a_i\) 转成二进制,然后从二进制最高位开始扫,对于第 \(x\) 位,如果 \(d_x\) 不存在,那么 \(d_x=a_i\),否则 \(a_i \gets a_i \oplus d_x\),一个一个插入即可。

最大值问题:贪心从大到小求解。

最小值问题:最小 \(d_i\) 或者是 0。

\(k\) 小问题:转换线性基后直接二进制拆分。

异或和问题:枚举二进制位后算贡献 \(2^{d} \times 2^{s-1}\)

posted @ 2022-04-17 17:56  Plozia  阅读(85)  评论(0编辑  收藏  举报