线性基
在线性代数中,对于向量组 a 1 , a 2 , . . . , a m a_1,a_2,...,a_m a1,a2,...,am我们把其张成空间的一组线性无关的基称为该向量的线性基,
用途
- 快速查询一个数是否可以被一堆数异或出来
- 快速查询一堆数可以异或出来的最大/最小值
- 快速查询一堆数可以异或出来的第k大值
性质
- 原数列里的任何一个数都可以通过线性基里的数异或表示出来
- 线性基里任意一个子集的异或和都不为0
- 一个数列可能有多个线性基,但是线性基里数的数量一定唯一,而且是满足性质一的基础上最少的
证明性质二:
如果集合内存在
a
1
⊕
a
2
⊕
.
.
.
⊕
a
m
=
0
a_1 \oplus a_2 \oplus...\oplus a_m=0
a1⊕a2⊕...⊕am=0,那么一定存在一个
a
k
a_k
ak使得
a
1
⊕
a
2
⊕
.
.
.
⊕
a
m
(
不
包
含
a
k
)
=
a
k
a_1 \oplus a_2 \oplus...\oplus a_m(不包含a_k)=a_k
a1⊕a2⊕...⊕am(不包含ak)=ak
原理 & 实现
将一个数插入线性基:
void insert(LL x)
{
for(int i = 62; ~i; -- i)
{
if(x & (1ll << i))
{
if(!p[i])
{
p[i] = x;
cnt ++;
break;
}
else x ^= p[i];
}
}
}
- 从高位到低位进行
- 如果 x ( 2 ) x_{(2)} x(2)第 i i i位为1,判断 p [ i ] p[i] p[i]是否插入,没有就插入并且退出,否则就异或上 p [ i ] p[i] p[i]去进行下一位操作 。
查询异或最大值
直接贪心异或即可
LL qmax()
{
LL ans = 0;
for(int i = 62; ~i; -- i)
ans = max(ans, ans ^ p[i]);
return ans;
}
查询异或最小值
一般来说线性基里的最小元素就是异或最小值,只有原数列里存在0是,最小值才是0(线性基里不能表示出0),我们直接在插入时记录一下即可
查询第k大异或和
只需按照 k k k的二进制分解去选即可,比如第 3 = ( 11 ) 2 3=(11)_2 3=(11)2小的元素就是排序之后的线性基的第一个和第二个的异或,排序后,线性基从小到大排列,因为 p p p数组的性质决定了没有两个最高位相同的数,所以这样可以求出区间第k大异或和
合并两个线性基
只需要将一个线性基中的元素插入到另一个线性基即可