HDU 3949 XOR

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\n",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:\n",c);
53         scanf("%d",&q);
54         while (q--) solve();
55     }
56     return 0;
57 }

猜你喜欢

转载自www.cnblogs.com/mjtcn/p/9260606.html
今日推荐