线性寄
作者:互联网
定义
线性基是一个数的集合,取线性基中若干个数异或起来可以得到原序列中的任何一个数。
我们设 \(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;}
}
}
}
标签:基中,xor,基的,int,异或,线性 来源: https://www.cnblogs.com/tttttttle/p/16296228.html