线性基学习笔记

重新学了下。

定义

线性基是对某个序列生成的一个集合,满足两个性质:

  1. 在线性基中任意选择若干个数的异或和的值所构成的集合,等于原序列中选择若干个数的异或和所构成的集合。
  2. 线性基是满足上述条件的最小集合

线性基数组的含义:

\(d_i\) 为线性基第 \(i\) 位,则 \(d_i\) 的含义是当前线性基内任意异或出来的数字中,最高位为 \(i\)任意一个数字。

推论/衍生性质

  1. 原序列中任何数都能通过线性基中若干个数异或得到。
  2. 设一个序列的线性基为 \(S\),则 \(\not \exists x,y\in S,x\oplus y =0\)。这里的 \(x\)\(y\) 可以不仅仅是一个数,而是一组数。
  3. 线性基大小最多\(\left \lceil \log_2 \max\{S\} \right \rceil\)。因为线性基最多一位上一个数。
  4. 线性基大小与插入顺序无关。如果在某种顺序下 \(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\)


其余的正在学。

posted @ 2025-08-14 15:00  _E_M_T  阅读(13)  评论(0)    收藏  举报