【线性基小记】
定义
对一个数的集合\(S\),其线性基就是由最少的数构成的集合\(B\),满足在\(S\)中任选一些数异或后的值,都可以在\(B\)中选一些数异或使得两个值相等。
性质
- \(B\)中任意元素异或起来结果不为\(0\)
ps:即要求\(B\)中元素线性无关,否则一定可以删掉至少一个数,使得\(B\)能表示的数集不变。 - 为了方便,我们钦定\(B\)集合中任意两个数的最高位不同(若存在相同,则可以用其中一个异或上另一个,然后往更低位去插入)
插入
利用性质2,我们设\(b_i\)为\(B\)中最高位为第\(i\)位的数,若没有,则为\(0\)。
初始\(S\)为空,\(B\)也为空,假设当前\(S\)新加入一个数\(x\),考虑\(B\)集合的变化:
从最高位开始,若\(x\)这一位上为\(0\),则不进行任何操作。否则,如果\(b_i\)有值,就令\(x\)异或上\(b_i\);如果\(b_i=0\),就令\(b_i=x\)
复杂度\(O(\log V)\)
点击查看代码
bool insert(int x)
{
for(int i=60;~i;--i)
{
if(x>>i&1) {
if(b[i]) x^=b[i];
else {
b[i]=x;return 1;//成功插入
}
}
}
return 0;//插入失败
}
推论
设\(S\)的值域上界为\(2^n-1\),则\(B\)最多有\(n\)个数
证明:考虑插入的时候记录的\(b_i\),当每一位上\(b_i\)都有值时,任意一个元素都不可能再插入到线性基中,也就是说\(n\)就是线性基大小的上限。
合并线性基
合并两个线性基\(S_1,S_2\),只需把一个集合中的元素全部插入另一个集合中,复杂度\(O(\log^2 V)\)
经典模型
- 求一个集合中能够异或出的最大值
从高位开始贪心,如果异或上\(b_i\),能使得结果更大,则异或。
点击查看代码
int get()
{
int x=0;
for(int i=50;~i;--i)
{
if((x^b[i])>x) x^=b[i];
}
return x;
}
- 求一个集合中能够异或出的第k小值(本质不同)
类似高斯消元,先把三角基消成对角基,然后把k二进制拆位,从低到高对应到线性基出现的位上,如果k的某一位为1,就异或上线性基上对应的数。
证明可以考虑从高到低,每一位选与不选刚好把剩下的所有可能分成两半,其中一半完全小于另一半。
特别的,要特判0能否得到,即原集合是否线性相关(或含有0)。
同时,可以得到推论:
一个集合能够异或出的数的个数为\(2^{|B|}\)(\(B\)为线性基的大小),前提是允许选空集,否则,要特判集合是否线性相关。
例题
P4570 [BJWC2011]元素
Sol:按照价值从大到小排序,如果能插入线性基则选上这个数。
证明:可以发现这个过程跟kruskal非常相似,证明可以类比kruskal的方法。
首先最多能够选的个数为这个集合的线性基大小,是固定的,而且取到这个上界,结果一定不劣。
假设当前的数能够插入线性基却不选,那么考虑最终的选取集合,一定可以在之后选取的数中选一个删去,然后把这个数加入,答案一定更优。(这里一组线性相关的数集,就相当于kruskal中的一个环)