知识串联:
- 基:域内的其他值可以用 “基” 线性表示
- 线性基:以 异或(xor) 为基础的运算
- 二维向量空间的基:最常见的是(1, 0) (0, 1) ;这样所有向量都可以用这两个向量表示!!当然还有其他的基,比如说:(2, 0),(0, 1);(2, 1),(0,2) 等……
- 举个例子:向量(3, 5)
- 1.以 a =(1,0)b =(0,1) 为基; (3,5)=3*(1,0)+5*(0,1)=3*a+5*b;
- 2.以 a =(2,1)b =(0,2)为基;(3,5)=(3/2)*(2,1) + (7/4) * (0,2) = (3/2) * a + (7/4) * b;
步如正题——线性基构造
问题:怎样构造基,能使其他数仅通过异或(xor)就能构造出来?
解决方法:类似于二维线性空间,第i位放1,其余放0
举个例子:4位二进制的基:1000 0100 0010 0001,这样无论什么数都可以被异或出来,比如0101 = (0100) xor (0001)…方法就是第几位有1,就异或(xor)第几个基!
BUT!!!这样做没有必要
类似于二维向量的基(2, 1)(0,2)一样,我们只需要保证第 i 位不为 0 ,高位都为0,低位无所谓
举个例子:4位二进制的基:1010 0110 0011 0001,这样无论什么数也可以被异或出来,比如说0101 = (0110) xor (0011)
方法:就是当前第几位有1,就异或(xor)第几个基,然后用结果继续运算直到为0000。
举个例子:(0100) xor (0110) = (0010)
(0010) xor (0011) = (0001)
(0001) xor(0001) = (0000)
BUT!!!这样做还有个问题 ——如果基是随意构造的话,那就没有了意义。
那么我们在给线性基提一个要求,要求所有的基都是由给定的数构造而来。
举个例子:由给定的1010 1111 1001 1100 构造线性基
方法:用第i个数构造基的时候,从前向后扫描,如果这一位有1就异或xor掉,没有就放入 线性基 中,如果异或成0000就不用加入,因为他可以用其他基表示。
第一个数1010, 直接放入基中(基:1010)
第二个数1111,与基中的数异或 (1111 xor 1010 = 0101) ,基中没有第2位为1的数,所以将0101放入基中(基:1010 0101)
第三个数1001,与基中的数异或(1001 xor 1010 = 0011),基中没有第3位为1的数,所以将0011放入基中(基:1010 0101 0011)
第四个数1100,与基中的数异或(1100 xor 1010 = 0110),但是基中存在第二位为1的数,所以将第二位异或了 (0110 xor 0101 =0011),但是基中也存在,第3位为1的数,所以将第3位异或了(0011 xor 0011 =0000),当结果为0000说明这个数可以由之前的数线性表示(1100 = 1010 xor 0101 xor 0011),如果加入基,不满足基线性无关的原则。
这样 线性基 中的基就是给定元素或者给定元素异或xor出的值,并且线性无关
代码实现
void Guass() {
for (int i=1;i<=sz;i++) {
for (int j=62;j>=0;j--) {
if ((A[i]>>j)&1) {
if (!P[j]) {P[j]=A[i]; break;}
else A[i]^=P[j];
}
}
}
for (int j=0;j<=62;j++) if (P[j]) r++;
}
应用
应用一:给出一个数组,询问一个数能否表示为数组中的某些数的异或和。
解法: 先求出线性基, 从二进制高位往低位判断,假设当前x的这一位是1,如果线性基中存在某个向量最高位的1在这一位,那么这个向量肯定要取,把x异或这个向量,否则没法组合出来。
理由:基中的元素也是由给定的数异或出来的,尽管不知道是由哪些数异或出来,但是这不重要
BZOJ2460 BZOJ2115
struct Base{
int x[31];
void Insert(int v){
if (!v) return;
for (int i = 30; i>= 0; i--){
if ((1 << i) & v){
if (x[i]) v ^= x[i];
else {
x[i] = v;
break;
}
}
}
}
int Query(int v) {//求给定数与基中数异或的最小值
for (int i = 30; i >= 0; i--){
if ((v ^ x[i]) < v) v ^= x[i];
}
return v;
}
}base;