【ybtoj】【线性基】k小异或和
题目
解题思路
线性基小知识
三大性质:1.原集合里的任何数都可以用线性基中某些数的异或和表示
2.线性基中任意数的异或和不等于0
3.线性基的大小只与原集合有关,大小固定且最小
用数组d存储线性基
逐一枚举集合中的元素,并尝试加入线性基
从高到低枚举二进制数x的每一位
如果x的第i位是1且d[i]位为空,x成功加入线性基
否则x异或上d[i](保证d[i]第i位为1,且是最高位)
对于本题,还需对线性基做处理
具体为,枚举d[i],若第j位为1,则异或上d[j]
最后还是原集合的线性基
求解过程:假如二进制数k的第i位为1,ans就异或上线性基中第i大的元素,ans即为所求
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int q,n,m,k,t,p;
long long x,d[100];
void add(long long x)
{
for (int i=60;i>=0;i--)
if (x&(1ll<<i))
if (d[i]) x=x^d[i];
else {
d[i]=x;
break;
}
}
long long ask(int k)
{
if (k==1&&p<n) return 0;
if (p<n) k--;
if (k>=(1ll<<p)) return -1;
long long ans=0;
for (int i=0;i<=60;i++)
if (d[i])
{
if (k%2==1) ans^=d[i];
k/=2;
}
return ans;
}
int main()
{
scanf("%d",&q);
while (q--)
{
t++,p=0;
scanf("%d",&n);
memset(d,0,sizeof(d));
for (int i=1;i<=n;i++)
{
scanf("%lld",&x);
add(x);
}
for (int i=1;i<=60;i++)
for (int j=i-1;j>=0;j--)
if (d[i]&(1ll<<j)) d[i]^=d[j];
for (int i=0;i<=60;i++)
if (d[i]) p++;
scanf("%d",&m);
printf("Case #%d:\n",t);
for (int i=1;i<=m;i++)
{
scanf("%d",&k);
printf("%lld\n",ask(k));
}
}
return 0;
}