洛谷 P3380 【模板】二逼平衡樹(樹套樹) 題解
阿新 • • 發佈:2021-01-03
一、題目:
二、思路:
很明顯這題是道模板題。在此採用常規思路,即線段樹套平衡樹。
具體來說就是線段樹的每一個節點x是一棵平衡樹,這棵平衡樹中的元素就是x所管理的區間中的元素。可以簡記為,外層維護位置,內層維護元素。
而線段樹的作用就是當我們查詢某一個區間\([L,R]\)時,該區間可以被線段樹劃分成若干個小區間\([l_i, r_i]\),我們分別在這些小區間所對應的平衡樹們上查詢答案,最終再將這些答案合併起來,得到最終大區間的答案。
更具體地:
- 求排名:加和。時間複雜度:\(O(\log^2 N)\)
- 求前驅:取max。時間複雜度:\(O(\log^2 N)\)
- 求後繼:取min。時間複雜度:\(O(\log^2 N)\)
- 求第k小:這個問題比較麻煩,不能直接合並。我們考慮二分答案,即二分出來一個值mid,看一看Rank(mid)和k的關係,再去調整二分割槽間。時間複雜度:\(O(\log^2N \times \log maxv)\)
當然,求第k小的操作複雜度有一些高。我們可以採用權值線段樹套平衡樹,即外層維護值,內層維護位置的方法來降低複雜度。博主以後可能會實現這種方法。
三、程式碼:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define mem(s, v) memset(s, v, sizeof s) inline int read(void) { register int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return f * x; } const int maxn = 5e6 + 5, inf = 2147483647; int a[maxn], n, m; namespace Balance_Tree { int son[maxn][2], fa[maxn], root[maxn], val[maxn], siz[maxn], cnt[maxn]; int sz; inline void update(int x) { siz[x] = siz[son[x][0]] + siz[son[x][1]] + cnt[x]; } inline int get(int x) { return son[fa[x]][1] == x; } inline void rotate(int x) { int y = fa[x], z = fa[y], k = get(x); son[y][k] = son[x][k ^ 1]; fa[son[y][k]] = y; fa[y] = x; son[x][k ^ 1] = y; fa[x] = z; if (z) son[z][son[z][1] == y] = x; update(y); update(x); } inline void splay(int i, int x, int goal) { for (int f; (f = fa[x]) != goal; rotate(x)) { if (fa[f] != goal) rotate((get(x) == get(f)) ? f : x); } if (!goal) root[i] = x; } inline int kth(int i, int k) { ++k; int now = root[i]; while (233) { if (siz[son[now][0]] >= k) now = son[now][0]; else { k -= siz[son[now][0]]; if (cnt[now] >= k) return now; k -= cnt[now]; now = son[now][1]; } } } inline int pre(int i, int x) { int now = root[i], ret = 0; while (now) { if (val[now] == x) { if (son[now][0]) { now = son[now][0]; while (son[now][1]) now = son[now][1]; ret = now; } break; } if (val[now] < x && (!ret || val[now] > val[ret])) ret = now; now = son[now][x > val[now]]; } return ret; } inline int nxt(int i, int x) { int now = root[i], ret = 0; while (now) { if (val[now] == x) { if (son[now][1]) { now = son[now][1]; while (son[now][0]) now = son[now][0]; ret = now; } break; } if (val[now] > x && (!ret || val[now] < val[ret])) ret = now; now = son[now][x > val[now]]; } return ret; } inline void find(int i, int x) { int now = root[i], ret = 0; while (now) { if (val[now] == x) { ret = now; break; } if (val[now] > x && (!ret || val[now] < val[ret])) ret = now; now = son[now][x > val[now]]; } splay(i, ret, 0); } inline int Rank(int i, int x) { find(i, x); return siz[son[root[i]][0]]; } inline void insert(int i, int x) { if (!root[i]) { val[++sz] = x; siz[sz] = cnt[sz] = 1; root[i] = sz; return; } find(i, x); if (val[root[i]] == x) { ++cnt[root[i]]; return; } val[++sz] = x; cnt[sz] = 1; son[sz][0] = son[root[i]][0]; fa[son[sz][0]] = sz; fa[sz] = root[i]; son[root[i]][0] = sz; splay(i, sz, 0); } inline void del(int i, int x) { find(i, x); if (cnt[root[i]] > 1) { --cnt[root[i]]; return; } int tmp1, tmp2; tmp1 = pre(i, x); splay(i, tmp1, root[i]); tmp2 = son[root[i]][1]; fa[tmp2] = tmp1; son[tmp1][1] = tmp2; fa[tmp1] = 0; root[i] = tmp1; update(tmp1); } } namespace Segment_Tree { #define lson (o << 1) #define rson (o << 1 | 1) int L[maxn], R[maxn]; inline void build(int o, int l, int r) { L[o] = l; R[o] = r; Balance_Tree::insert(o, inf); Balance_Tree::insert(o, -inf); if (l == r) return; int mid = (l + r) >> 1; build(lson, l, mid); build(rson, mid + 1, r); } inline int Rank(int o, int ql, int qr, int v) { if (ql <= L[o] && R[o] <= qr) { return Balance_Tree::Rank(o, v) - 1;/*注意不能直接加和*/ } int mid = (L[o] + R[o]) >> 1, ret = 0; if (ql <= mid) ret += Rank(lson, ql, qr, v); if (qr > mid) ret += Rank(rson, ql, qr, v); return (o == 1) ? ret + 1 : ret; } inline void update(int o, int q, int v) { if (L[o] == R[o]) { if (~a[q]) Balance_Tree::del(o, a[q]); Balance_Tree::insert(o, v); return; } int mid = (L[o] + R[o]) >> 1; if (q <= mid) update(lson, q, v); else update(rson, q, v); if (~a[q]) Balance_Tree::del(o, a[q]); Balance_Tree::insert(o, v); if (o == 1) a[q] = v; } inline int Pre(int o, int ql, int qr, int v) { if (ql <= L[o] && R[o] <= qr) { return Balance_Tree::val[Balance_Tree::pre(o, v)]; } int mid = (L[o] + R[o]) >> 1, ret = -inf;//ret賦初值要賦為-inf if (ql <= mid) ret = max(ret, Pre(lson, ql, qr, v)); if (qr > mid) ret = max(ret, Pre(rson, ql, qr, v)); return ret; } inline int Nxt(int o, int ql, int qr, int v) { if (ql <= L[o] && R[o] <= qr) { return Balance_Tree::val[Balance_Tree::nxt(o, v)]; } int mid = (L[o] + R[o]) >> 1, ret = inf; if (ql <= mid) ret = min(ret, Nxt(lson, ql, qr, v)); if (qr > mid) ret = min(ret, Nxt(rson, ql, qr, v)); return ret; } inline int kth(int o, int ql, int qr, int k) { int l = 0, r = 1e8; while (l < r) { int mid = (l + r + 1) >> 1; if (Rank(1, ql, qr, mid) <= k) l = mid; else r = mid - 1; } return l; } } int main() { mem(a, -1); n = read(); m = read(); Segment_Tree::build(1, 1, n); for (register int i = 1; i <= n; ++i) { Segment_Tree::update(1, i, read()); } int opt, l, r, k; while (m--) { opt = read(); switch(opt) { case 1: l = read(); r = read(); k = read(); printf("%d\n", Segment_Tree::Rank(1, l, r, k)); break; case 2: l = read(); r = read(); k = read(); printf("%d\n", Segment_Tree::kth(1, l, r, k)); break; case 3: l = read(); k = read(); Segment_Tree::update(1, l, k); break; case 4: l = read(); r = read(); k = read(); printf("%d\n", Segment_Tree::Pre(1, l, r, k)); break; case 5: l = read(); r = read(); k = read(); printf("%d\n", Segment_Tree::Nxt(1, l, r, k)); break; } } return 0; }