[模板]线性基
简化题意 \(:\)
给定一个集合,在其中选一个子集,使得子集的异或和最大.
线性基是个啥呢 \(?\)
线性基是一个集合,原集合中的任意元素相异或得到的值都能通过线性基中某几个元素异或起来得到,且线性基的大小最小.
相当于维护二进制下的每一位.
根据定义我们发现线性基就和向量的基底一样,一组二维向量基底能够表示该平面内的所有向量(通过向量的线性组合),一组线性基也同样可以表示原集合中所有数字的线性异或组合.
线性基如何构造呢 \(?\)
初始时为空,每插入一个元素,就检索线性基的每一位,如果待插入元素的该位为 \(0\) , 那么直接跳过,否则,检查线性基的该位是否为空,如果为空就直接赋为该元素,然后退出,插入结束.如果线性基不为空,那么表示待插入元素的该位已经被包含了,把该元素的这一位异或掉当前线性基的对应位(相当于抹除待插入元素的这一位),继续检索即可.
容易发现,一个待插入元素要么最后被异或为 \(0\) , 也即是此时的线性基已经包含了该元素,要么在中间插入成功.
所以我们也可以以此来判断一个元素是否被包含在线性基中.
插入代码 \(:\)
template < class T >
inline bool insert (T x) { // 返回值表示是否成功插入
if ( ! x ) return false ; // 显然插入一个 0 没有意义
for (int i = 62 ; ~ i ; -- i)
if ( x & ( 1ll << i ) ) {
if ( base[i] ) x ^= base[i] ;
else { base[i] = x ; return true ; } // 成功插入之后一定要及时退出
}
return false ;
}
查询所有子集中异或和的最大值呢?
考虑用 \(01\:trie\) 维护一对元素的异或最大值的时候,我们是从高位到低位贪心的.
那么线性基我们其实也是按位构造的,也可以从高位到低位贪心.
呃...然后就没了...
\(Code:\)
template < class T >
inline int qurery (T x) {
T res = 0ll ;
for (int i = 62 ; ~ i ; -- i)
res = max ( res , res ^ base[i] ) ;
return res ;
}