1. 程式人生 > 實用技巧 >洛谷 P3380 【模板】二逼平衡樹(樹套樹) 題解

洛谷 P3380 【模板】二逼平衡樹(樹套樹) 題解

一、題目:

洛谷原題

二、思路:

很明顯這題是道模板題。在此採用常規思路,即線段樹套平衡樹。

具體來說就是線段樹的每一個節點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;
}