1.异或空间相关概念
1.1 异或空间:依据线性空间的概念,我们可以进一步推广,不限于向量、向量加法和标量乘法。“异或空间”也是一个很常见的形式。异或空间是一个关于异或运算封闭的非负整数集合。可以在异或空间中用类似方法定义“表出” “线性无关” “基底” 等概念。(注:线性空间又称向量空间,是线性代数中的重要概念,用来处理代数运算以及其它更抽象的问题。)
1.2 表出:若整数b能由整数 a1,a2,...,ak 经异或运算得出,则成b能被 a1,a2,...,ak 表出。
1.3 线性相关与线性无关:任意选出异或空间中的若干个整数,如果其中存在一个整数能被其它整数表出,则称这些整数线性相关,否则线性无关。
2.相关考点
2.1 求基底:给定n个0~2^m-1之间的整数,a1,a2,...,an ,如何求出着n个整数表出的异或空间的基?
答:我们可以把它们看作m位二进制数,写成一个n行m列的01矩阵,矩阵第i行从左到右依次是ai的第m-1,m-2,...,1,0位(二进制数的最低位称为0位)。把矩阵作为系数矩阵,用高斯消元求解异或方程组的方法,将其转化为简化阶梯型矩阵。简化阶梯型矩阵每一行也是一个整数,所有非零整数一起构成异或空间的基底。
3.例题
原题链接:XOR HUOJ3949
解析:
用高斯消元求出异或空间的基底。从基底中选取若干个整数(假设t维),显然有 2^t 种方法,因此t维异或空间共有2^t个整数,每个数各不相同,并与每个取法一一对应。
而高斯消元过程中,共t个主元,而每个主元所对应的列的其它元素都为0,因此每个主元都位于该列最高位,设bi为消元后第 i 个基底,故b1 > b2 > ... > bt,同样的,设 ci 为第i个数主元所在的位置(最低位为0),c1 > c2 > ... > ct。如此一来我们只需将k进行二进制分解,就可以得到第k大的数了。
这里还需讨论一个情况,那就是异或空间是否含有0。造成这个原因的因为异或空间允许a^a,而本题不允许,也就是说本题不一定含0,而异或空间一定包含整数0。所以我们只需检查简化阶梯型矩阵是否存在“零行”,就可以判断是否可以通过不同的数异或运算得到0。如果可以,本题就该将k从k-1开始分解(按我们的写法0是第0大,而依据题意是第1大),否则从k开始分解(相当于忽略异或空间的0)。
代码示例:P161——《算法竞赛进阶指南》李煜东
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
unsigned long long a[10010];
int n, m, t, T;
int main() {
cin >> T;
for (int C = 1; C <= T; C++) {
cin >> n;
for (int i = 1; i <= n; i++) scanf("%I64u", &a[i]);
bool zero = 0;
t = n;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++)
if (a[j] > a[i]) swap(a[i], a[j]);
if (a[i] == 0) {
zero = 1, t = i - 1;
break;
}
for (int k = 63; k >= 0; k--)
if (a[i] >> k & 1) {
for (int j = 1; j <= n; j++)
if (i != j && (a[j] >> k & 1)) a[j] ^= a[i];
break;
}
}
cin >> m;
printf("Case #%d:
", C);
while (m--) {
unsigned long long k, ans = 0;
scanf("%I64u", &k);
if (zero) k--;
if (k >= 1llu << t) puts("-1");
else {
for (int i = t - 1; i >= 0; i--)
if (k >> i & 1) ans ^= a[t - i];
printf("%I64u
", ans);
}
}
}
}