[十二省联考2019] 异或粽子
[十二省联考2019] 异或粽子
第一次写 \(01\ Trie\) 诶.
这道题还是比较简单的, 毕竟我这种屑都能写出来.
题目要求前 \(k\) 大的区间异或和. 由于异或的性质, 我们将原数列做一个前缀异或和, 于是题目就转换成了选出 \(k\) 组 \((i, j), i \le j\) , 使 \(sum[i]\ xor\ sum[j]\) 最大, 然后由于这个 \(l \le r\) 的限制比较烦人, 并且 \(k \le \frac{n(n - 1)}{2}\) , 所以我们就可以转化成前 \(2k\) 大的, 不要求 \(l \le r\) . 最后再 \(/ 2\) 就是了.
然后将这个 \(n + 1\) 个前缀异或和 (包括 \(0\) ) 插入一个 \(01\ Trie\) , 就能在 \(O(\log a)\) 的时间内求出异或 \(s[i]\) 最大的 \(s[j]\) . 我们先把最大的 \(n + 1\) 组插入一个大根堆, 然后每次取出堆顶, 再插入堆顶的下一位, 共取出 \(2k\) 次.
复杂度 \(O(2k \log a)\)
\(code:\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll read() {
ll x = 0, f = 1;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
return x * f;
}
const int N = 2e7 + 5;
struct Trie {
int ch[N][2], sz[N], cnt;
void Insert(ll v) {
int x = 0;
for (int i = 33; i >= 0; i--) {
int y = (v >> i) & 1;
if (!ch[x][y]) ch[x][y] = ++cnt;
x = ch[x][y];
sz[x]++;
}
}
ll Query(ll v, int k) {
int x = 0;
ll ans = 0;
for (int i = 33; i >= 0; i--) {
int y = (v >> i) & 1;
if (k <= sz[ch[x][y ^ 1]]) {
x = ch[x][y ^ 1], ans |= 1ll << i;
}
else k -= sz[ch[x][y ^ 1]], x = ch[x][y];
}
return ans;
}
} t;
struct Node {
int id, rk;
ll val;
bool operator < (const Node &a) const {
return a.val > val;
}
};
int n, k;
ll a[N], ans;
priority_queue <Node> q;
int main() {
n = read(), k = read() << 1;
for (int i = 1; i <= n; i++) a[i] = a[i - 1] ^ read();
for (int i = 0; i <= n; i++) t.Insert(a[i]);
Node x;
for (int i = 0; i <= n; i++) {
x.id = i, x.rk = 1, x.val = t.Query(a[i], 1);
q.push(x);
}
for (int i = 1; i <= k; i++) {
x = q.top(); q.pop();
ans += x.val;
if (x.rk <= n) {
x.rk++, x.val = t.Query(a[x.id], x.rk);
q.push(x);
}
}
printf("%lld", ans >> 1);
return 0;
}
看不见我看不见我看不见我