线性基学习笔记

概念

线性基是向量空间的一组基,通常可以解决有关异或的一些题目。

性质

线性基有三大性质

\(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){
			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\n",ans);
}

\(2\)、查询 \(n\) 个数异或的最小值。

就是线性基中最小的值

如果有插入不成功的情况,答案就是 \(0\)

\(3\)、查询某个数是否能被异或得到。

看一下这个数能否成功插入线性基

如果可以,就能被异或得到,否则不能

\(4\)、查询 \(n\) 个数异或的 \(k\) 小值

首先,要对这个序列的线性基处理一下,对于每一个 \(d[i]\)

枚举 \(j=i \to 1\),如果 \(d[i]_{(2)}\) 的第 \(j\) 位为 \(1\)

那么 \(d[i]\ xor\ d[j-1]\)

其实就是让每一个线性基提供自己最高位上的一

这样,我们只需要把 \(k\) 进行二进制分解,贪心去选即可

异或之后得到的数组仍然是一个线性基

也就是说一个序列的线性基不唯一,只是元素数量唯一而已

题目

P4570 [BJWC2011]元素

利用了性质三,贪心地选择,既然数的个数是唯一的那么我们就可以按照魔法值的从大到小来选

P3857 [TJOI2008]彩灯

同样利用了性质三

P3292 [SCOI2016]幸运数字

线性基可以支持 \(RMQ\),巧妙地利用倍增可以做到两个 \(log\)

posted @ 2021-01-06 20:18  liuchanglc  阅读(149)  评论(1编辑  收藏  举报