题目html
求一段数列前 \(k\) 大区间异或和的和。node
本题和 【Luogu P2048】[NOI2010] 超级钢琴 思路极其类似。对于前 \(k\) 大,咱们能够用堆来维护。spa
不一样于超级钢琴的是,本题是异或和,不能直接根据前缀和大小排序,那怎么办呢?考虑用 0/1Trie 解决。从高位到低位插入数字,顺便记录子树大小,至于查询,毕竟是第 \(k\) 大,咱们能够像平衡树同样经过子树大小肯定当前一位。code
const int N = 2e7 + 10; int n, k; ll sum[N]; struct Trie { int ch[N][2]; ll siz[N], tot; void ins(ll val) { int u = 0; for (int i = 31; ~i; --i) { bool k = (val >> i) & 1; siz[u]++; if (!ch[u][k]) ch[u][k] = ++tot; u = ch[u][k]; } siz[u] ++; return; } ll query(ll val, int n) { int u = 0;ll ans = 0; for (int i = 31; ~i; --i) { bool k = (val >> i) & 1; if(!ch[u][k ^ 1]) u = ch[u][k]; else if (n <= siz[ch[u][k ^ 1]]) u = ch[u][k ^ 1], ans |= 1ll << i; else n -= siz[ch[u][k ^ 1]], u = ch[u][k]; } return ans; } }t; struct node { ll x, rk, val; inline bool operator < (const node& a) const { return val < a.val; } }; priority_queue<node> q; ll ans = 1; int main() { // freopen(".in", "r", stdin); // freopen(".out", "w", stdout); scanf ("%d%d", &n, &k); t.ins(0); for (int i = 1; i <= n; i++) scanf ("%lld", &sum[i]), sum[i] ^= sum[i - 1], t.ins(sum[i]); for (int i = 0; i <= n; i++) q.push((node){i, 1, t.query(sum[i], 1)}); for (int i = 1; i <= 2 * k; i++) { node x = q.top(); ans += x.val; q.pop(); if (x.rk < n) q.push((node){x.x, x.rk + 1, t.query(sum[x.x], x.rk + 1)}); } printf ("%lld\n", ans / 2); return 0; }