给你\(n\)个数,从其中随便取任意数问你第\(k\)小的异或和是多少。c++
这题是线性基的应用之一。咱们知道一个集合的线性基能够异或出这个集合的全部异或和,而且方法惟一。对于一个数x可否被异或出来,咱们能够这样作,假设x的最高位为r,那么在线性基里面找到最高为也为r的数,让x异或r。而后不断重复这个操做,若是最后x能为0那么确定是能的。而后咱们如今想一想怎么经过线性基求第k小的异或和。
首先,线性基能够当作一个最大线性无关组,也就是说最高位以上都是0。如今假设最大线性基是这样的:spa
00100010 - - - - - - 4
00011000 - - - - - - 3
00000110 - - - - - - 2
00000001 - - - - - - 1code
那么异或和排序显然是(用编号表示):
\(1\Rightarrow2\Rightarrow(2\bigoplus1)\Rightarrow3\Rightarrow(3\bigoplus1)\Rightarrow(3\bigoplus1\bigoplus2)\Rightarrow......\Rightarrow(4\bigoplus1\bigoplus2\bigoplus3)\)
这个排序显然是按照二进制最高位排序加上去的。可是若是想按这样的规律数显然是不行的。不过,咱们能够联系下上面说的一个数x可否组成的原理。若是我将线性基用离散化的思想处理,而后再按照上面的排序思想。就能够把题目转化乘k可否由离散后的线性基组成。
举个例子:咱们假设要求第k小的数,先上面的线性基离散化就能够当作排序
1000
0100
0010
0001it
而后,这个新的线性基能够组成\(2^5-1\)排名内的全部数。也就是原线性基全部能够组成的异或的数量。k必定在这里面,不然就不存在。所以,咱们只须要分解k的全部1就好了。class
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+5; long long a[maxn],p[maxn],tot; void Guass(int n) { memset(p,0,sizeof(p)); for(int i=1;i<=n;i++) { for(int j=63;j>=0;j--) { if((a[i]>>j)&1) { if(p[j]) a[i]^=p[j]; else { p[j]=a[i]; break; } } } } for(int i=63;i>=0;i--) { if(!p[i])continue; for(int j=i+1;j<=62;j++) { if((p[j]>>i)&1) p[j]^=p[i]; } } tot=0; for(int i=0;i<=63;i++) if(p[i]) p[tot++]=p[i]; } int main() { int T,i,j,n,Q; long long k; scanf("%d",&T); for(int s=1;s<=T;s++) { printf("Case #%d:\n",s); scanf("%d",&n); for(i=1;i<=n;i++) scanf("%I64d",&a[i]); Guass(n); scanf("%d",&Q); while(Q--) { scanf("%I64d",&k); if(n!=tot)k--; if(k>=(1ll<<tot))printf("-1\n"); else { long long ans=0; for(i=0;i<=63;i++) if((k>>i)&1) ans^=p[i]; printf("%I64d\n",ans); } } } }