线性基 学习笔记
0x00 前言
在信息学中,我们经常需要解决一些类似“选一些数让它们的异或和最大”的问题。这时,我们就需要用线性基来解决。
0x01 定义
注:下文中除非特殊说明,集合均指「无符号整数集」。
线性空间
可以简单地理解为一个集合。
生成子集
设 \(T\subseteq S\) ,所有这样的子集 \(T\) 的异或和组成的集合称为集合 \(S\) 的生成子集。
更形象地, \(S\) 的生成子集就是在 \(S\) 中选出任意个数,其异或和的所有可能的结果组成的集合。
线性相关
对于集合 \(S\) ,如果其中存在一个元素 \(x\) ,使得 \(S\) 在去除这个元素后得到的集合 \(S'\) 的生成子集中包含 \(x\) ,则称 \(S\) 线性相关;反之,称 \(S\) 线性无关。
更形象地,若集合 \(S\) 存在一个元素可以用其它若干个元素异或起来得到,则称 \(S\) 线性相关,反之无关。
线性基
线性无关的生成子集称为线性空间的基底,称为基(另一种定义是线性空间的极大线性无关子集)。
0x02 实现
插入
一般来说,我们常常采用动态插入元素的方法来构造线性基。即不断向已经得到的线性基里加入新元素,如果它可以被其他元素异或出来则舍去。
这里将介绍学习自 Pecco 的一种很优美且简洁的写法:
点击查看代码
struct LB{
vector<int>B;
void insert(int x){
for(auto b:B)x=min(x,b^x);
for(auto &b:B)b=min(b,b^x);
if(x)B.push_back(x);
}
};
这样构造出的集合,不仅满足任意元素都不能被其他元素异或出来的性质,它还有更好的性质:每个元素的最高有效位各不相同,且如果某一位是一个元素的最高有效位,则其他元素在这一位均为 0。这个性质可以归纳地证明:
空集时,这一性质显然成立;当向具有此性质的集合插入新元素时,如果你可以通过异或上其中一个数 \(b\) 使 \(x\) 变小,说明 \(x\) 在 \(b\) 这一位是 1。为了消掉这一位,此时你一定要异或。
于是一路贪心下去可以得到 \(x\) 与集合内元素的组合异或的最小值。如果最小值是 0,说明 \(x\) 可以被异或出来;否则 \(x\) 的最高有效位一定与其他元素各不相同,于是把它也插入集合,并把原集合中所有元素这一位消掉,使集合继续满足性质。
时间复杂度 \(\mathcal{O}(\log n)\) 。
合并
直接把一个全部插入到另一个里面即可,时间复杂度 \(\mathcal{O}(\log^2 n)\) 。
点击查看代码
LB merge(LB x,LB y){
LB res=x;for(auto b:y.B)res.insert(b);
return res;
}
询问
对于某个元素,选总是比不选更好,所以最大异或和就是整个集合的异或和。
如果要求第 \(k\) 小/大,也只需要将 \(k\) 二进制分解后选择对应线性基即可(但是如果线性基大小小于 \(n\),说明原数组可以异或出 0,需要 \(k\gets k-1\) )。
点击查看代码
int query(int k){//第k小
sort(B.begin(),B.end());
if((int)B.size()<n)k--;
int ans=0;for(auto x:B){if(k&1)ans^=x;k>>=1;}
return k?-1:ans;
}
完整版:
点击查看代码
int n;
struct LB{
vector<int>B;
void clear(){
B.clear();
}
void insert(int x){
for(auto b:B)x=min(x,b^x);
for(auto &b:B)b=min(b,b^x);
if(x)B.push_back(x);
}
int query(int k){
sort(B.begin(),B.end());
if((int)B.size()<n)k--;
int ans=0;for(auto x:B){if(k&1)ans^=x;k>>=1;}
return k?-1:ans;
}
}lb;
LB merge(LB x,LB y){
LB res=x;for(auto b:y.B)res.insert(b);
return res;
}
0x03 应用
P3812 【模板】线性基 & ACwing 210. 异或运算
模板题。
P4570 [BJWC2011]元素
P3265 [JLOI2015]装备购买
0x04 总结
可能不是那么重要,但是一旦考到不会就完全没思路了。