线性基
线性基用于求解这样的问题:在n个数中选任意个数,求满足某个性质的异或和。
什么是线性基?线性基是一个数集,其中若干个数异或起来可以得到原数集中任何一个数。
性质
原数集中任意一个数都可以由线性基里面的一些数异或得到。
线性基里面任意个数异或起来都不等于0.
线性基里面数的个数唯一,且在保证前面性质的基础上个数最少。
构造
这里直接给出线性基的构造方法
1 | void _insert(ll x) { |
问题求解
最大值
直接从高位到低位贪心,如果异或当前的线性基可以使得答案变大就异或。
即
1 | ll getmax() { |
为什么这样做是对的呢?考虑我们上面的构造过程,P[i]一定是考虑了更高位的线性基的,所以只要保证当前位会使答案增加即可,低位的总贡献无论如何都不会比一个高位的贡献大。
区间查询最大值
在求最大值的基础上,我们可以得到序列中求区间最大值的算法,该算法还支持动态扩大区间。
我们构造前缀线性基,即对每个位置i构造[1,i]的线性基,并且记录线性基上插入的数字的位置,保证这个位置尽量靠后。
更多实现细节看模板
1 | void _insert(int x, int loc) { |
最小值
这个很显然。
如果是问线性基能异或出的最小值,那就是最小的base
如果是问原数集能异或出的最小值,那就再看看有没有数不能插入线性基,如果有,最小值就是0。
第k小值
在前面构造的线性基的基础上进行改进处理,使得每一位上的线性基最高位就是这一位。
1 | void presolve() { |
查询的时候,就可以通过k的二进制来选择是否异或上某个线性基了。
1 | ll query(int k) { |
判断一个数能否被线性基异或得出
尝试将这个数插入线性基,能插入说明不能,不能插入说明能。
v1.5.2