线性基学习笔记
重新学了下。
定义
线性基是对某个序列生成的一个集合,满足两个性质:
- 在线性基中任意选择若干个数的异或和的值所构成的集合,等于原序列中选择若干个数的异或和所构成的集合。
- 线性基是满足上述条件的最小集合。
线性基数组的含义:
设 \(d_i\) 为线性基第 \(i\) 位,则 \(d_i\) 的含义是当前线性基内任意异或出来的数字中,最高位为 \(i\) 的任意一个数字。
推论/衍生性质
- 原序列中任何数都能通过线性基中若干个数异或得到。
- 设一个序列的线性基为 \(S\),则 \(\not \exists x,y\in S,x\oplus y =0\)。这里的 \(x\) 和 \(y\) 可以不仅仅是一个数,而是一组数。
- 线性基大小最多为 \(\left \lceil \log_2 \max\{S\} \right \rceil\)。因为线性基最多一位上一个数。
- 线性基大小与插入顺序无关。如果在某种顺序下 \(x\oplus y=z\) 导致 \(z\) 不能插入进线性基,则在另一种顺序中,\(y\oplus z=x\) 会导致 \(x\) 不能插入进线性基。
即虽然线性基内的数可能因为插入顺序不同,但大小是一样的。
实现
考虑如何构造线性基。构造线性基的原理是从最高位开始,考虑消掉 \(x\) 的每一位,直到消完为止。若无法消掉则插入进线性基当中。
代码流程如下:
从高到低位处理 \(x\)(二进制),对于第 \(i\) 位:
- \(x\) 的第 \(i\) 位是 \(0\),跳过。
- 否则,如果这一位上有线性基的元素,则将 \(x\oplus S_i\to x\) 消掉这一位。如果没有,则 \(x\to S_i\)。
代码如下:
int d[N];//线性基
void insert(int x)
{
f(i,55,0)
{
if(!(x&(1ll<<i))) continue;
if(d[i]) x^=d[i];
else
{
d[i]=x;
break;
}
}
}
构造线性基的复杂度是 \(O(n\log a_i)\)。
当 \(a_i\) 很大时,可以考虑用 bitset,此时复杂度为 \(O(n\dfrac{\log^2 a_i}{w})\)。
基本操作
求异或最大值
在一个序列 \(\{a_1,a_2,\cdots,a_n\}\),选取若干个数,求它们的最大异或和。
link。
思路:首先构造出线性基。
然后采取贪心思想,从最高位开始,如果当前答案异或线性基的这个元素可以变得更大,则把答案异或上这个元素。
一个 c++ 代码的典例如下:
int getmax()
{
int ans=0;
f(i,60,0) if(ans^p[i]>ans) ans^=p[i];
return ans;
}
求异或最小值
如果求线性基 \(S\) 内的元素能够异或出来的最小值,那么就是最小的 \(S_i\) 了,因为 \(\min\{S_i\}\) 无论异或谁都会变大。
如果求整个序列能够异或出的最小值的话,需要判断是否有元素能被线性基中的元素凑起来。如果有则答案为 \(0\),否则是 \(\min\{S_i\}\)。
求异或和 \(0\) 的非空子集个数
给定一个可重集 \(S\),求有多少个非空子集使得其异或和为 \(0\)。
假设组成线性基的元素为 \(M\),其余元素为 \(G\)。对任意 \(T_G\subseteq G\) 满足 \(\exists T_M\subseteq M \mid \bigoplus\limits_{x\in T_G}x=\bigoplus\limits_{y\in T_M} y\)。
显然 \(M\) 与 \(G\) 无交。
对于 \(S\) 的任意一个子集 \(T\),都可表示为 \(T=T_G\cup T_M\)。
显然不可能 \(T_G=\varnothing\)。
则考虑对每一个非空 \(T_G\),计算有多少个 \(T_M\) 满足其异或和为 \(0\)。
然后发现,每一个 \(T_G\),都有且仅有一个 \(T_M\) 满足其异或和为 \(0\),证明是显然的。
而题目中的不同只需要索引不同,因为每一个 \(T_G\) 的索引都完全不同,所以只需要计算非空 \(T_G\) 的个数即可。
显然是 \(2^{|G|}-1\)。
其余的正在学。

浙公网安备 33010602011771号