XOR
题意:
n个数,问任意异或后,第k小的数(去掉重复的异或值)。
分析:
线性基,先求出线性基。将线性基求出后,每个元素可以有取或不取两种选择(选到的异或起来就是构成的数),所有构成的数就是$2^{sz}$个,sz为线性基元素的个数。
所以第k小的就是将k二进制拆分后,有1的位值对应的元素。像二进制的构造一样,第一个二进制数时001,第二个是010,...,那么第k小的异或后的数,k的二进制位上1的位置对应的元素异或起来。
在其矩阵上可能存在对角线元素存在0的情况,那么最小的就是0,相应的k要-1。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 5 const int N = 100000 + 10; 6 const int m = 62; 7 8 LL a[N],b[100]; 9 vector<int>v; 10 bool flag = false; 11 12 void build(int n) { 13 memset(b,0,sizeof(b)); 14 v.clear(); 15 flag = false; 16 int cnt = 0; 17 18 for (int i=1; i<=n; ++i) 19 for (int j=m; j>=0; --j) 20 if ((a[i]>>j) & 1) { 21 if (b[j]) a[i] ^= b[j]; 22 else { 23 cnt++;b[j] = a[i]; 24 for (int k=j-1; k>=0; --k) if (b[k]&&((b[j]>>k)&1)) b[j] ^= b[k]; 25 for (int k=j+1; k<=m; ++k) if ((b[k]>>j)&1) b[k] ^= b[j]; 26 break; 27 } 28 } 29 30 flag = (cnt != n); 31 for (int i=0; i<=m; ++i) // 从小的往大的放。 32 if (b[i]) v.push_back(b[i]); 33 } 34 void solve() { 35 LL k;scanf("%lld",&k); 36 if (flag) k --; 37 int sz = v.size(); 38 if (k >= (1LL << sz)) {puts("-1");return ;} 39 LL ans = 0; 40 for (int i=0; i<sz; ++i) 41 if ((k>>i) & 1) ans ^= v[i]; 42 printf("%lld ",ans); 43 } 44 int main() { 45 46 int Case,n,q; 47 scanf("%d",&Case); 48 for (int c=1; c<=Case; ++c) { 49 scanf("%d",&n); 50 for (int i=1; i<=n; ++i) scanf("%lld",&a[i]); 51 build(n); 52 printf("Case #%d: ",c); 53 scanf("%d",&q); 54 while (q--) solve(); 55 } 56 return 0; 57 }