概念
线性基是向量空间的一组基,通常可以解决有关异或的一些题目。
性质
线性基有三大性质
(1)、原序列里面的任意一个数都可以由线性基里面的一些数异或得到
(2)、线性基里面的任意一些数异或起来都不能得到 (0)
(3)、线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的
线性基的题目一般都是对这三大性质的考察
要想了解性质,我们首先要知道线性基是如何构造的
构造
对原集合的每个数 (p) 转为二进制,从高位向低位扫,对于第 (x) 位是 1 的,如果 (a_x) 不存在,那么令 (a_x=p) 并结束扫描,如果存在,令 (p=p xor a_x)。
代码
void ad(long long now){
for(rg int i=60;i>=0;i--){
if(now==0) return;
if(now&1LL<<(i-1)){
if(d[i]){
now^=d[i];
} else {
d[i]=now;
return;
}
}
}
}
证明
对于性质一,分两种情况讨论
如果 (x) 没有插入成功,那么 (x) 必定在异或一些数后变成了 (0)
就有 (d[i] xor d[j] xor d[k]...xor x=0)
也就是 (d[i] xor d[j] xor d[k]...=x)
如果 (x) 插入成功
则 (d[i] xor d[j]...xor x=d[k])
变一下就是 (d[i] xor d[j] xor d[k]...=x)
对于性质二
如果 (d[i] xor d[j] xor d[k]=0)
那么较晚插入的那一个一定会被异或成 (0),不会插入线性基
对于性质三
目前不会证,建议看这篇博客
用途
(1)、查询 (n) 个数异或的最大值。
从高位到低位贪心地选,如果异或当前位的线性基后答案变得更大就异或上,否则就不选
代码
void cx(){
long long ans=0;
for(rg int i=60;i>=0;i--){
if((ans^d[i])>ans) ans^=d[i];
}
printf("%lld
",ans);
}
(2)、查询 (n) 个数异或的最小值。
就是线性基中最小的值
如果有插入不成功的情况,答案就是 (0)
(3)、查询某个数是否能被异或得到。
看一下这个数能否成功插入线性基
如果可以,就能被异或得到,否则不能
(4)、查询 (n) 个数异或的 (k) 小值
首先,要对这个序列的线性基处理一下,对于每一个 (d[i]) ,
枚举 (j=i o 1),如果 (d[i]_{(2)}) 的第 (j) 位为 (1)
那么 (d[i] xor d[j-1])
其实就是让每一个线性基提供自己最高位上的一
这样,我们只需要把 (k) 进行二进制分解,贪心去选即可
异或之后得到的数组仍然是一个线性基
也就是说一个序列的线性基不唯一,只是元素数量唯一而已
题目
利用了性质三,贪心地选择
同样利用了性质三
线性基可以支持 (RMQ)