題目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; }