一、题目
点此看题
题意:
给定一些数,求这些数的异或和的第
k
k
k小的数。
数据范围:
T
≤
30
,
1
≤
N
≤
10000
,
1
≤
Q
≤
10000
,
e
a
c
h
n
u
m
b
e
r
i
s
b
e
t
w
e
e
n
1
a
n
d
1
0
18
T\leq 30,1\leq N\leq 10000,1\leq Q\leq 10000,each\ number\ is\ between\ 1\ and\ 10^{18}
T≤30,1≤N≤10000,1≤Q≤10000,each number is between 1 and 1018
二、解法
我们优化线性基的维护方式,不是维护一个行列阶梯形矩阵,而是维护一个行简化阶梯形矩阵。
在插入当前基之前,用低位基消去这个基的低位,再用这个基消去高位基底的低位。
最大异或和是所有基底的异或和,最小异或和仍是最后一行。
能组合出的数目是
2
c
n
t
2^{cnt}
2cnt,
c
n
t
cnt
cnt是基数,把
k
k
k做二进制拆分即可。
然而还存在一个问题,
0
0
0是否真的存在。
我们只需要判断
c
n
t
=
n
cnt=n
cnt=n是否成立,如果是,那么
0
0
0将无法被组成,
k
k
k加
1
1
1。
但是在计算是我们默认
0
0
0可以达到,所以在二进制拆分是要保证
k
>
0
k>0
k>0。
#include <cstdio>
#include <cstring>
#define LL long long
LL read()
{
LL x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
LL T,n,m,x,Case,cnt,p[66];
void insert(LL x)
{
for(LL i=63;i>=0;i--)
{
if(!(x>>i&1)) continue;
if(!p[i])
{
p[i]=x;
for(LL j=i-1;j>=0;j--)
if(p[j]&&(p[i]>>j&1)) p[i]^=p[j];
for(LL j=i+1;j<=63;j++)
if(p[j]>>i&1) p[j]^=p[i];
break;
}
x^=p[i];
}
}
int main()
{
T=read();
while(T--)
{
memset(p,0,sizeof p);
n=read();cnt=0;
for(LL i=1;i<=n;i++)
insert(read());
for(LL i=0;i<=63;i++)
if(p[i]) cnt++;
m=read();
printf("Case #%lld:\n",++Case);
for(LL i=1;i<=m;i++)
{
x=read();
if(cnt==n) x++;
if((1ll<<cnt)<x)
{
puts("-1");
continue;
}
LL tmp=cnt,ans=0;
for(LL i=63;i>=0;i--)
if(p[i])
{
LL now=(1ll<<(tmp-1));
if(now<x) x-=now,ans^=p[i];
tmp--;
}
printf("%lld\n",ans);
}
}
}