HDU3949 XOR¶
题意¶
给定 n(n \le 10000) 个数 a_1, a_2, \ldots, a_n,以及 Q(Q\le 10000) 个询问,每次询问这些数(至少一个,不能不选)能够组成的异或和中第 k 小的数是什么(去掉重复的异或和)。
题解¶
学习线性基时做的一道入门例题。首先将这些数异或的向量空间的一组线性基\mathfrak{B}求出来,然后判断能否取零,也就是在求线性基时是否存在向量可以被前面的向量张成,如果可以取零,那么可取的数的数量可以多一,相当于k–-
,接着考虑如何求第k大的数,假设我们现在将线性基中不为零的数提取出来,会发现其实这个的运算大小也是与二进制类似的,因为异或没有进位,所以如果将线性基中的数按照大小排序为(v1,v2,\cdots ,v_m)的话取10000得到的结果肯定比取0xxxx得到的结果要大,这里的取什么指是否取线性基中这一位上的向量。
于是将k转化成二进制(x_0,x_1,x_2.\cdots x_m)答案就是XOR_{i=0}^{63} v_i\cdot x_i
程序¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | #include <cstdio> #include <iostream> #include <algorithm> #include <cmath> #include <cstring> #define MAX_N 10007 #define reg register typedef long long ll; int N, Q, top; ll a[MAX_N], b[64], c[64], tot; bool has; template <typename _T> inline void read (_T& x) { x = 0; reg _T y = 1; reg char c = getchar(); while (c < '0' || '9' < c) { if (c == '-') y = -1; c = getchar(); } while ('0' <= c && c <= '9') x = x * 10 + c - '0', c = getchar(); x *= y; } inline ll quick_pow (ll x, ll p) { ll ans = 1; while (p) { if (p & 1) ans *= x; p >>= 1; x *= x; } return ans; } inline void init () { memset(b, 0, sizeof(b)); for (int i = 1;i <= N; ++i) { for (int j = 63; j >= 0; --j) { if (a[i] >> j & 1) { if (b[j]) a[i] ^= b[j]; else { b[j] = a[i]; for (int k = j - 1; k >= 0; --k) if (b[k] && b[j] >> k & 1) b[j] ^= b[k]; for (int k = j + 1; k <= 63; ++k) if (b[k] >> j & 1) b[k] ^= b[j]; break; } } } if (a[i] == 0) has = true; } top = 0; for (int i = 0;i <= 63; ++i) if (b[i]) c[++top] = b[i]; tot = quick_pow(2, top); } inline ll solve (ll x) { ll res = 0; if (has) x--; if (x >= tot) return -1; int cnt = 0; while (x) { cnt++; res ^= ((x & 1) * c[cnt]); x >>= 1; } return res; } int main () { int T, cnt = 0; read(T); while (T--) { read(N); for (int i = 1;i <= N; ++i) read(a[i]); has = false; init(); read(Q); ll x; printf("Case #%d:\n", ++cnt); while (Q--) { read(x); printf("%lld\n", solve(x)); } } return 0; } |