本文共 2561 字,大约阅读时间需要 8 分钟。
在处理一系列权值点的集合操作时,我们需要在合并两个集合时,能够快速查询其中的第k大权值。传统的并查集难以满足这一需求,而结合并查集与线段树的方法可以有效解决问题。
我们采用并查集结构,每个集合对应一个线段树。这样,在合并两个集合时,可以直接将对应的线段树合并,确保在后续查询时能够快速找到第k大权值。
#includeusing namespace std;#define mid ((l + r) >> 1)const int maxn = 1e5 + 5;int sum[maxn < 5], ls[maxn < 5], rs[maxn < 5], rt[maxn], tot;void pushup(int t) { int tl = ls[t], tr = rs[t]; sum[t] = sum[tl] + sum[tr];}int add(int l, int r, int t, int p, int c) { int now = ++tot; ls[now] = ls[t]; rs[now] = rs[t]; if (l == r) { sum[now] += c; return now; } if (p <= mid) { ls[now] = add(l, mid, ls[now], p, c); } else { rs[now] = add(mid + 1, r, rs[now], p, c); } pushup(now); return now;}int ask(int l, int r, int t, int k) { if (l == r) return l; if (sum[ls[t]] >= k) { return ask(l, mid, ls[t], k); } else { return ask(mid + 1, r, rs[t], k - sum[ls[t]]); }}int mer(int a, int b, int l, int r) { if (!a) return b; if (!b) return a; if (l == r) { sum[a] += sum[b]; return a; } ls[a] = mer(ls[a], ls[b], l, mid); rs[a] = mer(rs[a], rs[b], mid + 1, r); pushup(a); return a;}
int a[maxn], f[maxn], n, m, id[maxn];int getf(int x) { return f[x] == x ? x : f[x] = getf(f[x]);}bool dsu_mer(int a, int b) { int fa = getf(a), fb = getf(b); if (fa == fb) return false; f[fb] = fa; mer(rt[fa], rt[fb], 1, n); return true;} int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", a + i); id[a[i]] = i; f[i] = i; rt[i] = add(1, n, rt[0], a[i], 1); } for (int i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); dsu_mer(x, y); } char op[5]; int q; scanf("%d", &q); for (int i = 1; i <= q; i++) { scanf("%s", op); scanf("%d%d", &x, &y); if (op[0] == 'Q') { x = getf(x); if (sum[rt[x]] < y) { printf("-1\n"); } else { printf("%d\n", id[ask(1, n, rt[x], y)]); } } else { dsu_mer(x, y); } } return 0;} 通过上述方法,我们可以在并查集合并过程中,高效地查询集合中的第k大权值。
转载地址:http://gadi.baihongyu.com/