线性基小结
前言
线性基里集合的线性组合能表示出原数组的异或组合
构造
依次插入数,从位数的高到低插入
如果该位无元素\((0)\),将目前元素放进去
如果改为有元素,异或后处理更低一位
解释构造与性质
插入失败:\(x\bigotimes d[a]\bigotimes d[b]\bigotimes d[c]...\bigotimes d[z]=0\)
插入成功:插入\(y=x\bigotimes d[a]\bigotimes d[b]\bigotimes d[c]...\bigotimes d[z]\ne 0\),利用\(y\)元素,实现异或全排列
所以线性基里的元素个数是最少的
在目前线性基中插入\(b\)和\(c\)均失败的情况下
变化线性基(拿出原数组中的一个元素)若插入其中一个成功则另一个不成功
假设要插入\(b\)和\(c\):\(b=d_i \bigotimes d_j \bigotimes d_k...\bigotimes d_z\)
把其中一个数删掉后,其实等同于自己替代了那个位置,\(c\)还是插不进
基础应用
异或最大值:贪心从高到低贪心选择或不选择
异或最小值:贪心从低到高位选择第一个非空元素
异或判特值:贪心处理直到为\(0\)
异或个数:因为每个元素都是处理出来相对独立的,答案为\(2^{集合个数}\)
异或\(K\)小值:\(2^i\)小值就是第\(i-1\)个元素贪心地与前面处理的结果,然后对\(K\)拆位处理
线性基合并:暴力插入即可
# include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL bit[64], a[64], x, k;
int main(){
int n, m, cnt=0;
scanf("%d",&n);
for(int i=0; i<n; ++i){
scanf("%lld",&x);
for(int j=50; ~j; --j){
if(x&(1LL<<j)){
if(!bit[j]) {bit[j] = x; break;}
x ^= bit[j];
}
}
}//普通构造
for(int i=50; ~i; --i)
for(int j=i-1; ~j; --j)
if(bit[i]&(1LL<<j))
bit[i] ^= bit[j];//
for(int i=0; i<=50; ++i)
if(bit[i])
a[cnt++] = bit[i];//
scanf("%d",&m);
while(m--){
scanf("%lld",&k);
if(cnt != n) --k;
if(k >= (1LL<<cnt)) puts("-1");
else{
LL ans = 0;
for(int i=0; i<cnt; ++i)
if(k&(1LL<<i)) ans ^= a[i];
printf("%lld\n",ans);
}
}
return 0;
}
应用
区间查询异或最值
解法一:离线处理排序右端点,每次插入的时候越末插入越优,所以尽量把向量换成新插入的点 code
解法二:在线处理,每次继承(复制)前面一个线性基,由于元素小不影响复杂度,如上插入 code
每个物品都有价值与标号,求在选出的集合子集标号异或不为\(0\)的最大价值
由性质可得一个贪心做法:不断插入到线性基,插入成功则答案加上价格
无向边权图\(1\)到\(n\)可重边路径异或最大值
预处理每个环的异或值,这个值是可以单独算的,而环与环之间的链其实通过两次异或能忽略不计
而\(1\)到\(n\)的链随便选就行了,如果有多条路径,这种环也可以通过异或得到最大链
然后通过线性基得到最大值