#114. k 大異或和
阿新 • • 發佈:2019-02-20
題目描述
這是一道模板題。
給由 n n n 個數組成的一個可重集 S S S,每次給定一個數 k k k,求一個集合 T⊆S T \subseteq S T⊆S,使得集合 T T T 在 S S S 的所有非空子集的不同的異或和中,其異或和 T1xorT2xor…xorT|T| 是第 k k k 小的。
輸入格式
第一行一個數 n n n。
第二行 n n n 個數,表示集合 S S S。
第三行一個數 m m m,表示詢問次數。
第四行 m m m 個數,表示每一次詢問的 k k k。
輸出格式
輸出 m m m 行,對應每一次詢問的答案,第 k k k 小的異或和。如果集合 S S S 的所有非空子集中,不同的異或和數量不足 k k k,輸出 −1 -1 −1。
樣例
樣例輸入
3
1 2 3
5
1 2 3 4 5
樣例輸出
0
1
2
3
-1
資料範圍與提示
1≤n,m≤105,0≤Si≤250 1 \leq n, m \leq 10 ^ 5, 0 \leq S_i \leq 2 ^ {50} 1≤n,m≤105,0≤Si≤250
大意:
在原有線性基上繼續構造,構造規則為
若i < j, aj的第j位是1,就把aj異或上ai。查詢的時候將k二進位制拆分,對於1的位,就異或上對應的線性基。
最終得出的答案就是k小值。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 100000 + 5;
const int maxm = 60;//最大50
const int top = 55;
long long n,m;
long long xxj[maxm];
long long a[maxn];
bool cmp(long long x,long long y) {
return x > y;
}
long long ans;
long long all;
long long full;
bool zero;
long long rank[maxn];
int main() {
cin>>n;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
sort(a+1,a+1+n,cmp);
zero = 1;
for(int i=1;i<=n;i++) {
for(int j=top;j>=1;j--) {
if((a[i] >> (j-1)) & 1) {
if(xxj[j]) {
a[i] ^= xxj[j];
if(a[i] == 0) zero = 1;
}
else{
xxj[j] = a[i];
break;
}
}
}
}
for(int i=1;i<=top;i++) if(xxj[i]) {
all ++;
rank[all] = i;
}
for(int i=1;i<=all;i++) {
full <<= 1;
full += 1;
}
for(int i=top;i>=1;i--) {
if(!xxj[i]) continue;
for(int j=i - 1;j>=1;j--) {
if((xxj[i] >> (j-1)) & 1) {
xxj[i] ^= xxj[j];
}
}
}
cin>>m;
for(int i=1;i<=m;i++) {
ans = 0;
long long k;
scanf("%lld",&k);
if(zero) {
k --;
if(k > full) {
printf("-1\n");
continue;
}
if(k == 0) {
printf("%lld\n",ans);
continue;
}
}
if(k > full) {
printf("-1\n");
continue;
}
for(int i=1;i<=top;i++) {
if((k >> (i - 1)) & (long long)1) ans += xxj[rank[i]];
}
printf("%lld\n",ans);
}
return 0;
}