线性基求第k小异或值

题目链接
题意:给由 n 个数组成的一个可重集 S,每次给定一个数 k,求一个集合 \(T \subseteq S\)
使得集合 T 在 S 的所有非空子集的不同的异或和中,
其异或和 \(T_1 \mathbin{\text{xor}} T_2 \mathbin{\text{xor}} \ldots \mathbin{\text{xor}}T_{|T|}\)是第 k 小的。


/*

1.照例建立线性基
2.使得线性基中有且只有base[i]的第i位为1
3.记录所有有值的base[] 从低位到高位记为0~cnt,共cnt + 1个 (注:闭区间
这时线性基可以构成的数有(1 << cnt) + 1个,如果cnt + 1 < n的话 说明可以取零 这时可以构成的数有(1 << (cnt + 1))个
4.取 第k小 时
如果k大于可以构成的数的总数 那么无解
否则res是所有base[i] ((k - 1)的第i位为1) 的异或和 

*/

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 55;
int n, m;

struct BASE{
	long long w[N];
	int cnt;
	void init(){
		memset(w, 0, sizeof(w));
	}
	void ins(long long x){
		for(int i = 50; i >= 0; --i){
			if((x >> i) & 1) 
				if(w[i]) x ^= w[i];
				else {w[i] = x; break;}
		}
	}
	void build(){
		for(int i = 50; i >= 0; --i){
			if(!w[i]) continue;
			for(int j = i + 1; j <= 50; ++j){
			    if((w[j] >> i) & 1){
			    	w[j] ^= w[i];
			    }
			}
		}
		//for(int i = 0; i <= 5; ++i) printf("%d %lld\n", i, w[i]);
		for(int i = 0; i <= 50; ++i){
			//printf("%d %lld\n", i, w[i]);
			if(w[i]){
				w[cnt++] = w[i];
		    //	printf("%d %lld\n", cnt - 1, w[cnt - 1]);
			}
		}
		--cnt;
	}
	long long query(long long x){
		long long res = 0;
		for(int i = cnt; i >= 0; --i){
			if((x >> i) & 1) res ^= w[i];
		}
		return res;
	}
}base;

int main() {
	base.init();
	scanf("%d", &n);
	long long x;
	for(int i = 1; i <= n; ++i){
		scanf("%lld", &x);
		base.ins(x);
	}
	base.build();
	scanf("%d", &m);
	for(int i = 1; i <= m; ++i){
		scanf("%lld", &x);
		if(n != base.cnt + 1) --x;//注意是非空子集 所以特判可否取零 
		if(x >= (1ll << (base.cnt + 1))) printf("-1\n"); //这种情况下无法取到 
		else printf("%lld\n", base.query(x));
	}
	return 0;    
} 
posted @ 2019-03-14 14:09  hjmmm  阅读(1010)  评论(0编辑  收藏  举报