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

1. 前言

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

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

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

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

2. 详解

2.1 定义与性质

线性基的定义如下:

给出一个数列 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,若一个序列 d 1 , d 2 , . . . , d k d_1,d_2,...,d_k d1,d2,...,dk 满足以下性质:

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

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

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

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

2.2 构造线性基

这个方法需要用到一个性质: a ⊕ b = c ⇔ a ⊕ c = b a \oplus b=c \Leftrightarrow a \oplus c=b ab=cac=b

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

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 ⊕ b = c ⇔ a ⊕ c = b a \oplus b=c \Leftrightarrow a \oplus c=b ab=cac=b 来证明的。

由于 x ⊕ d i = d j ⇔ d i ⊕ d j = x x \oplus d_i = d_j \Leftrightarrow d_i \oplus d_j = x xdi=djdidj=x,因此性质 1 得证。

性质 2 采用反证法:假设 d i ⊕ d j ⊕ d k = 0 d_i \oplus d_j \oplus d_k=0 didjdk=0 d k d_k dk 最晚插入。

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

上式等价于 d i ⊕ d j = d k d_i \oplus d_j= d_k didj=dk

于是根据代码, d k d_k dk 不应该被插入线性基,矛盾。


性质 3 的证明:

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

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

3. 应用

3.1 最大值问题

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

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

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

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

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

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

因此贪心是正确的。

3.2 最小值问题

这个问题有两类:

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

这个直接就是最小的 d i d_i di,因为最小的 d i d_i di 无论异或谁都会变大。

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

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

3.3 第 k 小问题

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

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

对于每一个 d i d_i di,如果 d i d_i di 二进制表示第 j j j 位为 1,那么 d i ← d j − 1 ⊕ d i d_i \gets d_{j-1} \oplus d_i didj1di

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

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

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

3.4 异或和问题

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

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

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

4. 总结

线性基的三大性质:

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

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

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

最小值问题:最小 d i d_i di 或者是 0。

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值