【ybtoj】【线性基】k小异或和

【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
#include
#include
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;
}

你可能感兴趣的:(线性基,ybtoj)