zoukankan      html  css  js  c++  java
  • 洛谷 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 imes 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
    ", Segment_Tree::Rank(1, l, r, k)); break;
                case 2: l = read(); r = read(); k = read(); printf("%d
    ", 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
    ", Segment_Tree::Pre(1, l, r, k)); break;
                case 5: l = read(); r = read(); k = read(); printf("%d
    ", Segment_Tree::Nxt(1, l, r, k)); break;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Nginx访问日志、 Nginx日志切割、静态文件不记录日志和过期时间
    nginx的安装 、Nginx默认虚拟主机、nginx用户认证、nginx 域名重定向
    lnmp架构、mysql的安装、php的安装、nginx相关
    限定某个目录禁止解析php 、限制user_agent 、php的配制文件、PHP的动态扩展模块
    配置url防盗链、目录权限访问控制Directory、文件访问权限控制FilesMatch
    指令的概述
    点击事件交互示例
    日期过滤器示例
    in和not in注意事项
    mysql里的case用法详解
  • 原文地址:https://www.cnblogs.com/little-aztl/p/14225197.html
Copyright © 2011-2022 走看看