1. 程式人生 > >異或空間與高斯消元

異或空間與高斯消元

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:\n", 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\n", ans);
			}
		}
	}
}