简述
线性相关:可以通过一些元素异或出集合中其它元素。
线性基的一些性质:
- 线性基内所有元素线性无关
- 对于原集合任意元素异或的值域和线性基任意元素异或的值域相同
- 线性基的个数就是二进制下位的个数
- 线性基a[i]如果存在,则满足第i位是1,且前面的位都是0
不用太在意如何求出的线性基
一些操作
最大值查询可以询问当前元素,只需要把ret改成当前元素即可
最小应该也可以用类似的贪心方法
对于第K小的判定的理解
对于第i位的元素,如果选择,将会带来2^i个较大元素,所以如果K第i位有元素,就需要选择当前线性基
注意
- 第一位应该是第0位
- 注意位运算要对1ll做
- 线性基要求不能异或出0,所以需要特判0,在插入的时候判断时候有0元素
论线性基和二进制trie的区别:
都是处理异或运算的手段
二进制trie是查找当前元素与一堆元素里面某一元素的最大异或和;
线性基是查找一堆元素任意选取一个子集的最大异或和
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int oo=60;/=log2n
struct LinearBasis{
static const int maxn=65;
bool o;//o=0表示没有0
int cnt;
LL d[maxn],p[maxn];
void Initial()
{
cnt=o=0;
memset(d,0,sizeof(d));
memset(p,0,sizeof(p));
}
bool Insert(LL val)//在原集合中插入val
{
for(int i=oo;i>=0;i--)
{
if(val&(1ll<<i))
{
if(!d[i])
{
d[i]=val;
break;
}
val^=d[i];
}
}
if(!val)o=1;
return val;//成功则返回true
}
LL query_max()//贪心策略:从大往小只要当前元素更大,则异或
{
LL ret=0;
for(int i=oo;i>=0;i--)
ret=max(ret,ret^d[i]);
return ret;
}
LL query_min()
{
if(o)return 0;
for(int i=0;i<=oo;i++)
if(d[i])
return d[i];
return 0;
}
void rebuild()//这应该是还原成高斯消元后的结果?需要在找第K大的时候使用
{
for(int i=oo;i>=0;i--)
for(int j=i-1;j>=0;j--)
if(d[i]&(1ll<<j))
d[i]^=d[j];
for(int i=0;i<=oo;i++)
if(d[i])p[cnt++]=d[i];
}
LL Kth(LL k)//查询第K小的元素
{
LL ret=0;
if(o)k--;
if(k>=(1ll<<cnt))//一共有2^cnt-1个元素
return -1;
for(int i=oo;i>=0;i--)//如果K的第i位有元素,则异或
if(k&(1ll<<i))ret^=p[i];
return ret;
}
}lb;
typedef LinearBasis L_B;
L_B Merge(const L_B &n1,const L_B &n2)//两个线性基合并:暴力插入
{
L_B ret=n1;
for(int i=oo;i>=0;i--)
if(n2.d[i])ret.Insert(n2.d[i]);
return ret;
}