隐藏
「LibreOJ114」k大异或和 - 线性基 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「LibreOJ114」k大异或和 - 线性基

题目大意

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


题目分析

看清楚题了,求的是$k$小!
先对$S$求线性基$B$,注意题目中描述的是非空子集,因此要判断$\vec0$是否在$B$的张成里。
根据定义,若$S$存在一个向量组满足线性相关性,那么就可以通过异或得到$0$。
如果$\left|B\right|\neq n$,那么说明存在一个向量组满足线性相关性,否则不存在。
若存在,那么共有$2^{\left|B\right|}$个异或和,否则仅有$2^{\left|B\right|}-1$个异或和。

假设线性基$B$从小到大为$(\vec{v_0},\vec{v_1},\ldots,\vec{v_{\left|B\right|-1}})$,第$k=(b_x\cdots b_0)_2$小的数根据二进制思想可以得到是:


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<climits>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;

typedef long long LL;

inline const LL Get_Int() {
LL num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}

const int MAX_BASE=50;

struct Linear_Bases {
LL b[MAX_BASE+5],a[MAX_BASE+5];
int sum=0;
bool build(vector<LL> a) { //返回张成是否包含0
for(LL num:a)
for(int j=MAX_BASE; j>=0; j--)
if(num>>j&1) {
if(b[j]) { //该位存在基
num^=b[j];
continue;
}
sum++;
b[j]=num;
for(int k=j-1; k>=0; k--)if(b[j]>>k&1)b[j]^=b[k];
for(int k=j+1; k<=MAX_BASE; k++)if(b[k]>>j&1)b[k]^=b[j];
break;
}
return sum!=a.size();
}
void split() {
int cnt=0;
for(int i=0; i<=MAX_BASE; i++)
if(b[i])a[cnt++]=b[i];
}
} lb;

int t,n;
vector<LL>a;

int main() {
n=Get_Int();
for(int i=1; i<=n; i++)a.push_back(Get_Int());
bool bj=lb.build(a);
lb.split();
t=Get_Int();
while(t--) {
LL k=Get_Int();
if(bj)k--;
if(k>(1ll<<lb.sum)-1) {
puts("-1");
continue;
}
LL ans=0;
for(int i=lb.sum-1; i>=0; i--)
if(k>>i&1)ans^=lb.a[i];
printf("%lld\n",ans);
}
return 0;
}
姥爷们赏瓶冰阔落吧~