测试地址:XOR
题目大意:给定
n
n
个正整数,它的每个子集(除空集外)都有一个异或和,求这些异或和中严格第小的值。
做法:本题需要用到异或线性基。
首先我们求出线性基,然后每种异或和对于这个线性基都有唯一的组合方式,而根据线性基的性质,它们的最高位肯定各不相同,并且没有任何一个数包含其他数的最高位,所以我们对
k
k
<script type="math/tex" id="MathJax-Element-3">k</script>进行二进制拆分,然后从小到大把对应位的线性基异或起来就是答案了。
傻逼之处:long long右移64位会溢出……这个怎么可能会知道啊……
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxbit=63;
ll a[10010],b[80];
int T,n,q,tot;
void work()
{
memset(b,0,sizeof(b));
for(int i=1;i<=n;i++)
for(int j=maxbit;j>=0;j--)
if ((a[i]>>j)&1)
{
if (b[j]) a[i]^=b[j];
else
{
b[j]=a[i];
for(int k=0;k<j;k++)
if (b[k]&&((b[j]>>k)&1)) b[j]^=b[k];
for(int k=j+1;k<=maxbit;k++)
if ((b[k]>>j)&1) b[k]^=b[j];
break;
}
}
tot=-1;
for(int i=0;i<=maxbit;i++)
if (b[i]) b[++tot]=b[i];
tot++;
}
int main()
{
scanf("%d",&T);
int t=0;
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
work();
printf("Case #%d:\n",++t);
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
ll x;
scanf("%lld",&x);
if (tot!=n) x--;
if (x>=(1LL<<tot)) printf("-1\n");
else
{
ll ans=0;
for(int j=0;j<tot;j++)
if ((x>>j)&1) ans^=b[j];
printf("%lld\n",ans);
}
}
}
return 0;
}