「BZOJ2733」「洛谷3224」「HNOI2012」永無鄉【線段樹合並】
阿新 • • 發佈:2019-04-28
如果 \n www. oid bzoj2733 name sca 一個 str
題目鏈接
【洛谷】
題解
很明顯是要用線段樹合並的。
對於當前的每一個連通塊都建立一個權值線段樹。
權值線段樹處理操作中的\(k\)大的問題。
如果需要合並,那麽就線段樹暴力合並,時間復雜度是\(nlogn\),均攤下來就是\(logn\)。
判斷聯通性的問題就用並查集來解決。
如果在同一個聯通塊裏,就不能合並,否則會出一點問題。
代碼
#include <bits/stdc++.h> using namespace std; const int N = 3000000 + 6; int rt[N], id[N], fa[N]; int n, m, v[N], k; char opt[5]; namespace seg { int ncnt = 0; struct node { int lc, rc, sz; node() { lc = rc = sz = 0; } } tr[N << 1]; void pushup(int nod) { tr[nod].sz = tr[tr[nod].lc].sz + tr[tr[nod].rc].sz; } void ins(int &nod, int l, int r, int k) { if (!nod) nod = ++ ncnt; if (l == r) { tr[nod].sz = 1; return; } int mid = (l + r) >> 1; if (k <= mid) ins(tr[nod].lc, l, mid, k); else ins(tr[nod].rc, mid + 1, r, k); pushup(nod); } int kth(int x, int l, int r, int rk) { if (l == r) return l; int mid = (l + r) >> 1; if (tr[tr[x].lc].sz >= rk) return kth(tr[x].lc, l, mid, rk); else return kth(tr[x].rc, mid + 1, r, rk - tr[tr[x].lc].sz); } int merge(int x, int y) { if (!x) return y; if (!y) return x; tr[x].lc = merge(tr[x].lc, tr[y].lc); tr[x].rc = merge(tr[x].rc, tr[y].rc); pushup(x); return x; } } int gf(int x) { return x == fa[x] ? x : fa[x] = gf(fa[x]); } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i ++) { scanf("%d", &v[i]); fa[i] = i; id[v[i]] = i; } for (int i = 1, a, b; i <= m; i ++) { scanf("%d%d", &a, &b); int x = gf(a), y = gf(b); fa[x] = y; } for (int i = 1; i <= n; i ++) seg::ins(rt[gf(i)], 1, n, v[i]); scanf("%d", &k); while (k --) { scanf("%s", opt); int x, y; scanf("%d%d", &x, &y); if (opt[0] == 'Q') { int z = gf(x); if (seg::tr[rt[z]].sz < y) puts("-1"); else { int t = seg::kth(rt[z], 1, n, y); printf("%d\n", id[t]); } } else { int a = gf(x), b = gf(y); if (a == b) continue; fa[a] = b; rt[b] = seg::merge(rt[a], rt[b]); } } return 0; }
「BZOJ2733」「洛谷3224」「HNOI2012」永無鄉【線段樹合並】