HDU - 3949 XOR 线性基
这里讲解一下线性基是如何求取第 $k$ 小的:
首先,我们构建出线性基,然后从高位枚举 $d[i]$ 的每一位,发现如果有 $j<i$ 且 $d[i]$ 在二进制中的 $j$ 处为 $1,$ 则异或掉 $d[j].$
这么做会得到一个新的线性基,根据定理,线性基中元素互相异或,异或集合不变,所以是正确的.
然后,你会得到一个序列,将序列中每一个元素都整合到一起,开始查询.
你会发现新的序列中第 $i$ 个元素代表的恰好是第 $2^{i-1}$ 小的异或值.
而由于我们已经将高位含有低位的部分全部给异或没了,所以说每一次在新的线性基中异或低位只会使值更大,所以我们将 $k$ 二进制拆分,依次异或掉就是正确的了.
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define M 62
#define N 100
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int n,m;
ll d[N];
void insert(ll x)
{
int i,j;
for(i=M;i>=0;--i)
{
if(x&((ll)1<<i))
{
if(!d[i])
{
d[i]=x;
break;
}
else x^=d[i];
}
}
}
void init()
{
int i,j;
for(i=M;i>=0;--i)
{
for(j=i-1;j>=0;--j)
if(d[i]&(1ll<<j))
d[i]^=d[j];
}
m=0;
for(i=0;i<=M;++i) if(d[i]) d[m++]=d[i];
}
ll query(ll k)
{
if(k==1&&m<n) return 0;
if(m<n) --k;
if(k>=(1ll<<m)) return -1;
ll re=0;
for(int i=M;i>=0;--i)
if((k&(1ll<<i)) && d[i])
re^=d[i];
return re;
}
void solve()
{
ll a;
int i,j,q;
scanf("%d",&n);
for(i=1;i<=n;++i)
scanf("%lld",&a),insert(a);
init();
scanf("%d",&q);
for(i=1;i<=q;++i)
scanf("%lld",&a),printf("%lld\n",query(a));
memset(d,0,sizeof(d));
}
int main()
{
int i,j,cas,T;
// setIO("input");
scanf("%d",&T);
for(cas=1;cas<=T;++cas)
{
printf("Case #%d:\n",cas);
solve();
}
return 0;
}

浙公网安备 33010602011771号