題解 P3224 [HNOI2012]永無鄉
阿新 • • 發佈:2021-08-13
前置知識: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