Loading

HDU 3949:XOR(高斯消元+线性基)

题目链接

题意

给出n个数,问这些数的某些数xor后第k小的是谁。

思路

高斯消元求线性基。

学习地址

把每个数都拆成二进制,然后进行高斯消元,如果这个数字这一位(列)有1,那么让其他数都去异或它,消掉这一列的1,使得最后得到的矩阵某一行如果那一列有1的话,那么其他行是不会有1的(就是线性基)。

最后得到一个行数row,代表总共有row个1。

这个证明还没想通,直接用了。

如果得到的row == n的话,代表每一个数都有一个1,那么是取不到0的,这个时候只能得到 2^row - 1 个数,否则其他时候都可以取0。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int N = 1e4 + 11;
LL a[N];

int Gauss(int n) {
	int row = 1;
	for(int k = 63; k >= 0; k--) {
		for(int i = row; i <= n; i++) {
			if((a[i] >> k) & 1) {
				swap(a[i], a[row]);
				for(int j = 1; j <= n; j++)
					if(j != row && ((a[j] >> k) & 1))
                        a[j] ^= a[row];
				row++;
				break;
			}
		}
	}
	return row - 1;
}

LL solve(int row, int n, LL k) {
    if(row < n) {
        if(k == 1) return 0;
        k--;
    }
	if((1LL << row) <= k) return -1;
	LL ans = 0;
	for(int i = 63; i >= 0; i--)
		if((k >> i) & 1) ans ^= a[row-i];
	return ans;
}

int main() {
	int t; scanf("%d", &t);
	for(int cas = 1; cas <= t; cas++) {
		int n; scanf("%d", &n);
		for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
		int row = Gauss(n);
		int q; scanf("%d", &q);
		printf("Case #%d:\n", cas);
		while(q--) {
			LL k; scanf("%lld", &k);
			printf("%lld\n", solve(row, n, k));
		}
	} return 0;
}
posted @ 2017-10-11 10:03  Shadowdsp  阅读(283)  评论(0编辑  收藏  举报