时隔半年蒟蒻还是没能完全理解线性基的原理,线性代数学的太差QAQ
但为了备考省选,还是稍微总结一下线性基的用法吧
线性基
给定一个原集合S,S的线性基能使用异或运算来表示原数集S使用异或运算能表示的所有数
如此大大缩小对原数集异或运算的查询
线性基的性质
- 线性基内的数相互异或得到的集合与原数集得到的异或集合相同
- 线性基内元素 a i a_i ai(第i位的元素,且不为0)其二进制表示下第 i i i位为1
- 线性基的异或集合内不存在0
-
若原数集大小为n,线性基大小为k,则共有 2 k 2^k 2k种不同的异或和,每个异或和出现次数为 2 n − k 2^{n-k} 2n−k
线性基插入
从高位向低位遍历线性基
如果
x
x
x二进制第
i
i
i位为1,检查线性基第
i
i
i位
若线性基第
i
i
i位为0,在该位插入
x
x
x并退出,否则令
x
x
x异或线性基第
i
i
i位数值
void ins(lt x)
{
for(lt i=62;i>=0;--i)
if(x&((lt)(1)<<i))
{
if(!d[i]){d[i]=x;break;}
else x^=d[i];
}
}
存在性查询
查询原数集的异或集合是否存在 x x x
按照插入的方式遍历一次线性基
若过程中发现
x
=
0
x=0
x=0,则表示当前线性基已经可以通过异或得到原先的
x
x
x
bool query(lt x)
{
for(lt i=62;i>=0;--i)
if(x&((lt)(1)<<i)){
x^=a[i];
if(x==0) return true;
}
return false;
}
查询异或最值
最大值
采用贪心思路
初始令
r
e
s
=
0
res=0
res=0,由高位向低位遍历线性基
若res异或该位能使res增大,则令res异或该位数值
lt qmax(node tt)
{
lt res=0;
for(int i=62;i>=0;--i)
if((res^tt.d[i])>res) res^=tt.d[i];
return res;
}
最小值
线性基中的最小值,即为原集合相互异或能得到的最小值
第K小异或值
HDU - 3949 XOR
蒟蒻看的最懵逼的一部分,求第k小还要重构出新的基,以及令人懵逼的flag变量QAQ
void ins(lt x)
{
for(lt i=62;i>=0;--i)
if(x&(1ll<<i)){
if(!a[i]){ a[i]=x; return;}
else x^=a[i];
}
flag=1;
}
void rebuild()
{
for(lt i=62;i>=0;--i)
{
if(!a[i])continue;
for(lt j=i-1;j>=0;--j)
{
if(!a[j])continue;
if(a[i]&(1ll<<j)) a[i]^=a[j];
}
}
for(int i=0;i<=62;++i)
if(a[i]) b[tot++]=a[i];
}
lt kth(lt k)
{
rebuild();
if(flag) k--;
if(k==0) return 0;
if(k>=(1ll<<tot)) return -1;
lt res=0;
for(int i=0;i<tot;++i)
if(k&(1ll<<i)) res^=b[i];
return res;
}