题目大意
给定一个长度为 n 的数列 a1,a2,⋯,an,问区间异或和前 k 大的区间的异或和之和。
数据范围:n≤5×105,m≤2×105,0≤ai<232。
思路分析
我们设 Si 表示前 i 个数的异或和,那么区间 [l,r] 的异或和等于 Sl−1⨁Sr。由于 x⨁y=y⨁x,本题的答案等于取 2k 个 Si⨁Sj 最大的 (i,j)(0≤i,j≤n,i≠j) 求和再除以 2。又由于 x⨁x=0,所以 i≠j 的限制也可以去掉。这样,我们使用字典树 + 堆来维护即可。时间复杂度 O((n+m)logn)。
代码实现
1 |
|
2 |
|
3 |
|
4 | using namespace std; |
5 | typedef long long ll; |
6 | typedef pair<ll, int> P; |
7 | |
8 | const int maxn = 5e5, maxm = 32, maxk = maxn * maxm; |
9 | int n, k, tot, ch[maxk + 3][2], sz[maxk + 3], cnt[maxn + 3]; |
10 | ll a[maxn + 3]; |
11 | priority_queue<P> H; |
12 | |
13 | void insert(ll num) { |
14 | int x = 1; |
15 | for (int i = maxm - 1; ~i; i--) { |
16 | int k = num >> i & 1; |
17 | if (!ch[x][k]) { |
18 | ch[x][k] = ++tot; |
19 | } |
20 | x = ch[x][k], sz[x]++; |
21 | } |
22 | } |
23 | |
24 | ll query(int y, ll num) { |
25 | int x = 1; |
26 | ll ans = 0; |
27 | for (int i = maxm - 1; ~i; i--) { |
28 | int k = num >> i & 1; |
29 | if (y <= sz[ch[x][k ^ 1]]) { |
30 | ans |= 1ll << i; |
31 | x = ch[x][k ^ 1]; |
32 | } else { |
33 | y -= sz[ch[x][k ^ 1]]; |
34 | x = ch[x][k]; |
35 | } |
36 | } |
37 | return ans; |
38 | } |
39 | |
40 | int main() { |
41 | scanf("%d %d", &n, &k), k *= 2; |
42 | for (int i = 1; i <= n; i++) { |
43 | scanf("%lld", &a[i]), a[i] ^= a[i - 1]; |
44 | } |
45 | tot = 1; |
46 | for (int i = 0; i <= n; i++) { |
47 | insert(a[i]); |
48 | } |
49 | for (int i = 0; i <= n; i++) { |
50 | cnt[i] = 1; |
51 | H.push(P(query(1, a[i]), i)); |
52 | } |
53 | ll ans = 0; |
54 | for (int i; k--; ) { |
55 | P x = H.top(); |
56 | H.pop(), i = x.second; |
57 | ans += x.first, cnt[i]++; |
58 | if (cnt[i] <= n + 1) { |
59 | H.push(P(query(cnt[i], a[i]), i)); |
60 | } |
61 | } |
62 | printf("%lld\n", ans / 2); |
63 | return 0; |
64 | } |