洛谷 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;
}
posted @ 2021-01-03 12:42  蓝田日暖玉生烟  阅读(94)  评论(0编辑  收藏  举报