感觉 也是相当有意思的一个数据结构,这里也不是小 trick,就是记录一些 的应用。
持续更新中~
# P5283 [十二省联考 2019] 异或粽子
一道经典题目,介绍一个比较简单的做法。
先做一个前缀异或和,题目就变成了找到 前 大的点对,且 。
的条件并不好处理,所以把这个上三角形对称一下,变成完整的矩形,然后就变成了找到前 大的点对。
具体操作时,建一个大根堆,对于第 行,把与其异或最大值插到堆中。
每次删除堆顶最大值,假设堆顶是第 行的第 大值,那么就把第 行的第 大值再插入堆中。
以此类推,找到前 个点对即可,最后答案再除以 2。
时间复杂度 。
# CF241B Friends
是上面那道题加强版。
考虑先把第 大值找到。那么二分第 大值,枚举每一个数,在 上找到与当前数异或值大于等于 的个数,最后跟 比较大小即可。
这部分时间复杂度 ,也可以直接 上二分,但是我不会。
然后要计算所有异或值大于等于 的和是多少。
先预处理一个数组 ,表示在 上 的子树中 出现了多少次。这样就可以在 的复杂度下计算一棵子树异或一个值的和是多少了。
这部分时间复杂度也是 。
小坑点: 会爆 ,所以二分 时需要开 。
#include <bits/stdc++.h> | |
#define ll long long | |
using namespace std; | |
const int mod = 1e9 + 7; | |
const int inv2 = 5e8 + 4; | |
const int N = 5e4 + 10; | |
inline int add(int x) {return x >= mod ? x - mod : x;} | |
inline int sub(int x) {return x < 0 ? x + mod : x;} | |
inline int mul(int x, int y) {return 1ll * x * y % mod;} | |
int n, k, ans; | |
int a[N]; | |
int ch[N * 20][2], siz[N * 20], tot; | |
int tr[N * 20][35]; //tr [x][y]: x 的子树中 2^y 出现了几次 | |
inline void ins(int x){ | |
int u = 0; | |
for(int i = 30; i >= 0; --i){ | |
int t = (x >> i) & 1; | |
if(!ch[u][t]) ch[u][t] = ++tot; | |
u = ch[u][t], siz[u]++; | |
} | |
} | |
inline int qry(int x, int v){ | |
int u = 0, res = 0; | |
for(int i = 30; i >= 0; --i){ | |
int y = (x >> i) & 1, b = (v >> i) & 1; | |
if(!b) res += siz[ch[u][y ^ 1]], u = ch[u][y]; | |
else u = ch[u][y ^ 1]; | |
if(!u) break; | |
} | |
res += siz[u]; | |
return res; | |
} | |
inline ll chk(int x){ | |
ll cnt = 0; | |
for(int i = 1; i <= n; ++i) cnt += qry(a[i], x); | |
return cnt / 2; | |
} | |
inline void dfs(int u, int dep, int z){ | |
if(!u) return; | |
if(!dep){ | |
for(int i = 0; i <= 30; ++i) | |
if((z >> i) & 1) tr[u][i] = siz[u]; | |
return; | |
} | |
dfs(ch[u][0], dep - 1, z); | |
dfs(ch[u][1], dep - 1, z | (1 << (dep - 1))); | |
for(int i = 0; i <= 30; ++i) | |
tr[u][i] = tr[ch[u][0]][i] + tr[ch[u][1]][i]; | |
} | |
inline int qrysum(int x, int kth){ | |
int u = 0; int res = 0; | |
for(int i = 30; i >= 0; --i){ | |
int y = (x >> i) & 1, b = (kth >> i) & 1; | |
if(!b){ | |
int v = ch[u][y ^ 1]; | |
for(int j = 0; j <= 30; ++j){ | |
if((x >> j) & 1) res = add(res + mul((siz[v] - tr[v][j]), (1 << j))); | |
else res = add(res + mul(tr[v][j], (1 << j))); | |
} | |
u = ch[u][y]; | |
}else u = ch[u][y ^ 1]; | |
if(!u) break; | |
} | |
res = add(res + mul(siz[u], kth)); | |
return res; | |
} | |
signed main(){ | |
#ifndef ONLINE_JUDGE | |
freopen("test.in", "r", stdin); | |
freopen("test.out", "w", stdout); | |
#endif | |
ios :: sync_with_stdio(false); | |
cin >> n >> k; | |
for(int i = 1; i <= n; ++i) cin >> a[i], ins(a[i]); | |
if(!k) return cout << 0 << endl, 0; | |
int l = 0, r = (1 << 30), kth = 0; | |
while(l <= r){ | |
int mid = (l + r) >> 1; | |
if(chk(mid) >= k) kth = mid, l = mid + 1; | |
else r = mid - 1; | |
} | |
dfs(ch[0][0], 30, 0), dfs(ch[0][1], 30, (1 << 30)); | |
for(int i = 1; i <= n; ++i) ans = add(ans + qrysum(a[i], kth)); | |
ans = mul(ans, inv2); | |
cout << ((ans - mul((chk(kth) - k), kth)) % mod + mod) % mod << endl; | |
return 0; | |
} |