线性寄

定义

线性基是一个数的集合,取线性基中若干个数异或起来可以得到原序列中的任何一个数。

我们设 \(P\) 为线性基的集合,\(a\) 为原数组。

那么 \(a_i\) 都能被若干个 \(P\) 内的元素的异或和表示出来。

用途:

通常可以解决有关异或的一些题目。

线性基的性质:

  • \(P_i\) 的最高位不同。
  • \(P_i\) 中没有异或和为 \(0\) 的子集。

算法流程:

前置知识:

a ^ b = c  等价于 a = c ^ b
a ^ b ^ b = a

异或的简单性质,至于证明 ~这你都不会快退役叭

我们设 \(P_i\) 代表的是最高有效位为第i位的线性基的向量

浅显易懂

首先我们要满足原数组的数能被线性基的子集表示。

当我们要插入一个数 \(x\)

  • 从高位向低位判断,直到遇到该元素某位上为 \(1\),设该位为 \(i\)
  • 然后判断 \(p_i\) 是否有值,如果没有把 \(x\) 存到 \(p_i\) 中,否则将 \(x\)\(p_i\) 异或然后重复上面的操作。

来证明一下为啥这样构造是对的,也就是为啥都能用线性基表示出来:

  • 如果 \(p_i\) 无值,则 \(p_i = a_i\) ,显然能表示出来。

  • 如果 \(p_i\) 有值, \(a_i = a_i\ xor\ p_i\),然后假设他会在 \(p_{i - j}\) 被加进线性基中,\(p_{i- j} = a_i \ xor \ p_i\)

    然后 \(p_i \ xor\ p_{i -j} = p_i \ xor\ a_i \ xor \ p_i = a_i\)。所以 \(a_i\) 也被表示出来了。

    所以该构造方案是正确的。

于是我们可以写出代码:

void add(int x) {
	for(int i = 63; i >= 0; i--) {
		if(x & (1ll << i)) {
			if(p[i]) x ^= p[i];
			else { p[i] = x; break;}
		}
	}	
}
posted @ 2022-05-21 22:22  TLE_Automation  阅读(36)  评论(0)    收藏  举报