题目链接
前置知识:kruskal 重构树,主席树
来讲一种目前题解区里没有,使用 kruskal 重构树和主席树,且时间复杂度为一个 log 的做法。
题目大意是给定一张点数为 \(n\) ,初始边数为 \(m\) 的无向图,图中每个点有一个权值,然后有 \(q\) 个操作,每个操作可能是询问与点 \(x\) 联通的点中权值第 \(k\) 小的点的编号,也可能是加入一条无向边。
看到这种与联通性相关的题,自然能想到 kruskal 重构树,于是把操作离线,连边操作直接丢给 kruskal 重构树,把其对应点的限制设为时间戳,然后将询问操作也带上时间戳存下来。
这样我们就解决了动态加边的问题,再给 kruskal 重构树加个倍增,我们就可以快速得到一个询问所对应的点集了。
询问的是第 \(k\) 小,于是在 kruskal 重构树上再跑个 dfs 序,弄颗主席树,此题就做完啦 ^_^
时间复杂度:\(O(qlog(n))\)
此外,实现时有个坑点,这张图不一定联通,所以 dfs 时一定要把每个根都跑了。
参考代码:
#include <bits/stdc++.h> #define rei register int #define ll long long using namespace std; const int inf = 0x3f3f3f3f; const int N = 1e5 + 5, M = 1e5 + 5, MAXQ = 3e5 + 5; int n, m, Q, tot, totq, num; int val[N * 2], fa[N * 2], f[N * 2][19], lim[N * 2]; int lson[N * 2], rson[N * 2], siz[N * 2]; int id[N * 2], key[N]; struct Segment_Tree { int tot, ver[N * 2]; struct node { int cnt, ls, rs; } T[N * 39]; int build(int l, int r) { int nnow = ++tot; if(l == r) return nnow; int mid = (l + r) >> 1; T[nnow].ls = build(l, mid); T[nnow].rs = build(mid + 1, r); return nnow; } int add(int x, int k, int l, int r, int now) { int nnow = ++tot; T[nnow] = T[now]; if(l == r) { T[nnow].cnt += k; return nnow; } int mid = (l + r) >> 1; int &L = T[nnow].ls, &R = T[nnow].rs; if(x <= mid) L = add(x, k, l, mid, T[now].ls); else R = add(x, k, mid + 1, r, T[now].rs); T[nnow].cnt = T[L].cnt + T[R].cnt; return nnow; } int ask(int k, int l, int r, int now1, int now2) { if(l == r) { return key[l]; } int L1 = T[now1].ls, R1 = T[now1].rs; int L2 = T[now2].ls, R2 = T[now2].rs; int mid = (l + r) >> 1; if(k <= T[L2].cnt - T[L1].cnt) { return ask(k, l, mid, L1, L2); } else { return ask(k - T[L2].cnt + T[L1].cnt, mid + 1, r, R1, R2); } } } SMT; int find(int x) { if(fa[x] == x) return x; return fa[x] = find(fa[x]); } void link(int u, int v, int l) { if(tot == 2 * n - 1) return; u = find(u), v = find(v); if(u == v) return; fa[u] = fa[v] = f[u][0] = f[v][0] = ++tot; fa[tot] = f[tot][0] = tot; lim[tot] = l, val[tot] = inf; lson[tot] = u, rson[tot] = v; } struct query { int x, k, high; query(int x = 0, int k = 0, int high = 0) { this->x = x, this->k = k, this->high = high; } } q[MAXQ]; void dfs(int x) { id[x] = ++num, siz[x] = 1; if(val[x] == inf) SMT.ver[num] = SMT.ver[num - 1]; else key[val[x]] = x, SMT.ver[num] = SMT.add(val[x], 1, 1, n, SMT.ver[num - 1]); if(lson[x]) dfs(lson[x]), siz[x] += siz[lson[x]]; if(rson[x]) dfs(rson[x]), siz[x] += siz[rson[x]]; } int solve(query pb) { int x = pb.x, k = pb.k, high = pb.high; for(rei i = 17; i >= 0; --i) { if(lim[f[x][i]] < high) { x = f[x][i]; } } int l = SMT.ver[id[x] - 1], r = SMT.ver[id[x] + siz[x] - 1]; if(k > SMT.T[r].cnt - SMT.T[l].cnt) return -1; return SMT.ask(k, 1, n, l, r); } int main() { ios::sync_with_stdio(false), cin.tie(NULL); cin >> n >> m; tot = n; for(rei i = 1; i <= n; ++i) { cin >> val[i]; fa[i] = f[i][0] = i; } for(rei i = 1; i <= m; ++i) { int u, v; cin >> u >> v; link(u, v, 0); } cin >> Q; for(rei i = 1; i <= Q; ++i) { char op; int x, y; cin >> op >> x >> y; if(op == 'B') { link(x, y, i); } else { q[++totq] = query(x, y, i); } } for(rei j = 1; j <= 17; ++j) { for(rei i = 1; i <= tot; ++i) { f[i][j] = f[f[i][j - 1]][j - 1]; } } SMT.ver[0] = SMT.build(1, n); for(rei i = 1; i <= tot; ++i) { if(fa[i] == i) dfs(i); } for(rei i = 1; i <= totq; ++i) { cout << solve(q[i]) << "\n"; } return 0; }
无耻求波赞 QAQ