线性基

0x00 问题引入

[模板]线性基

题目大意:给定\(n\)个数,求在这些数中选取任意个,使得他们的异或和最大

等价的说法就是,求这个集合中某个子集的异或和最大值

0x01 思路

注意到本题的数据范围是\(n≤10000\),暴力枚举有\(2^n\)种情况,无法通过此题

但是注意到\(2^{15}>10000\),也就是说所有组合异或的结果是非常小的,由于异或是一种不进位加法运算,所以不可能使得结果的二进制位增加,因此结果的二进制位数之多为二进制位数最多的那个数的位数(我们设为\(m\))

线性基的思路就是将\(2^n\)种组合变成\(2^m\)种组合,把对\(n\)个数的组合求异或和转化为对于\(m\)个数求异或和

0x02 概念

不妨设原数组为\(A=\{a_1,a_2,a_3,...,a_n\}\),如果我们找到了一个满足其组合的异或和和\(A\)相同的集合\(P=\{p_1,p_2,p_3,...,p_m\}\),那么我们就称\(P\)\(A\)的异或空间线性基

例如,\(A=\{2,3,5,6,7\}\) , \(n=5\),它的所有组合做异或和运算,结果为\(\{0,1,2,3,4,5,6,7\}\),\(A\)种最大的数有\(m=3\)位,用后面的方法可以求出\(P=\{5,2,1\}\),异或结果同样为\(\{0,1,2,3,4,5,6,7\}\)。注意到线性基并不是唯一的,\(P=\{7,2,1\}\),\(P=\{7,3,1\}\)同样是\(A\)的线性基

  1. 若整数\(b\)能由整数\(a_1,a_2,...,a_k\)经过异或运算得出 , 则称\(b\)可以被\(a_1,a_2,...,a_k\)表出 , \(a_1,a_2,...,a_k\)能表出的所有整数构成一个异或空间 , \(a_1,a_2,...,a_k\)被称为这个异或空间的生成子集

  2. 任意挑出异或空间中若干个整数 , 若存在一个可以被其它整数表出 , 则称这些整数线性相关 , 否则称这些整数线性无关 . 异或空间的就是异或空间中一个线性无关的生成子集 , 或者定义为异或空间的极大线性无关子集

0x03 性质

  1. 等价性:在\(A\)上进行运算的结果和在\(P\)上运算的结果是相同的
  2. 最小性:\(P\)是满足性质1的所有集合中元素个数最少的 . 同时说明了线性基存在线性无关性
  3. 线性基中不存在异或和为0的子集 . 若存在\(p_x\) ^ \(p_y\) ^ \(p_z\) ^ \(p_a=0\) , 那么 \(p_a\) = \(p_x\) ^ \(p_y\) ^ \(p_z\) , 表明\(p_a\)可以被其它三个值的异或和替代 , 和性质2矛盾 , 故性质3成立
  4. 线性基中每个元素的二进制位数均不同 . 如果若干个数最高位都是1 , 那么实质上只有最多一个数能对这一位做贡献 , 那么换成更低位的也是可以的
  5. 对于一个集合\(A=\{a_1,a_2,...,a_n\}\) , 若选择任意\(i\) , \(j\) , 将 \(a_j\)\(a_j\) ^ \(a_i\) , 不会改变运算结果

0x04 实现

我们已经了解了线性基的概念和性质 , 接下来我们想要求出某个数组的异或空间线性基

Plan A

我们以性质\(5\)为切入点 , 想出来的第一种方法就是利用高斯消元。我们先把 \(n\) 个数的二进制形式拼成一个\(n × m\)的矩阵 , 然后我们把这个矩阵当成高斯消元中的系数矩阵 , 进行消元将其变成简化阶梯矩阵即可

Plan B

另外一种

我称之为 "插入法"

直接结合代码来理解

bool insert(ll x){
    for(int i=B-1;i>=0;i--){ //B指的是当前是第几位
        if(x & (1ll << i)) {//若这一位为1就尝试插入
            if(num[i] == 0) {num[i] = x; return true;}//若线性基的这一位没有被填入,就把x塞进去,并且告诉你这个数是有用的
            x ^= num[i];//否则我们直接异或这一位填入的数
        }
    }
    return false;//如果到最后都没有,说明这个数没用
}

关于正确性 :把一个数和另一个数做异或后再插入进去 , 由性质 \(5\) , 我们得出来的集合的运算结果和原来的不会变 , 满足了性质 \(1\) , 同时异或可以将无用的最高位的 \(1\) 消去 , 满足性质 \(2\)

总之线性基对线性代数有较高要求 , 没有基础的大概感性理解以下就行了

注意 :

  1. 由性质 \(3\) , 线性基无法表出 \(0\) , 所以对 \(0\) 有关的要特判

  2. 线性基中数的个数一定是 \(m\) , 但数可以改变

0x05 应用

应用 1 :查询一个元素是否可以被异或出来

从高到低,如果这一位为11就异或上这一位的线性基,把11消去,根据性质一,如果最后得到了00,那这个数就可以表示出来

inline int ask(LL x) {
	for(R int i=62;i>=0;i--) 
		if(x&(1LL<<i)) x^=p[i];
	return x==0;
}

应用 2 :查询异或最大值

按位贪心即可

inline LL askmx() {
	LL ans=0;
	for(R int i=62;i>=0;i--)
		if((ans^p[i])>ans) ans^=p[i];
	return ans;
}

应用 3 :查询异或最小值

其实异或的最小值一般来说就是线性基里的最小元素,因为插入这个元素的时候我们总是尽量让它的高位为00才来插入这一位。但是为什么是“一般”呢?因为有可能会有出现00,得要在插入的时候记下个标记来特判才行

inline LL askmn() {
	if(zero) return 0;
	for(R int i=0;i<=62;i++)
		if(p[i]) return p[i];
}
posted @ 2022-11-09 18:10  羊扬羊  阅读(34)  评论(0)    收藏  举报