「luogu3380」【模板】二逼平衡树(树套树)

「luogu3380」【模板】二逼平衡树(树套树)

传送门
我写的树套树——线段树套平衡树。
线段树上的每一个节点都是一棵 \(\text{FHQ Treap}\) ,然后我们就可以根据平衡树的基本操作以及线段树上区间信息可合并的性质来实现了,具体细节看代码都懂。
参考代码:

#include <algorithm>
#include <cstdlib>
#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
template < class T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while ('0' > c || c > '9') f |= c == '-', c = getchar();
    while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
    s = f ? -s : s;
}

const int _ = 50010, __ = 2000010, INF = 2147483647;

int n, m, A[_];
int tot, ch[2][__], siz[__], pri[__], val[__];

struct node {
    int rt, a, b, c;

    inline int Newnode(int v) { return siz[++tot] = 1, val[tot] = v, pri[tot] = rand(), tot; }

    inline void pushup(int p) { siz[p] = siz[ch[0][p]] + siz[ch[1][p]] + 1; }

    inline void split(int p, int v, int& x, int& y) {
        if (!p) { x = y = 0; return ; }
        if (val[p] <= v) return x = p, split(ch[1][p], v, ch[1][x], y), pushup(p);
        else return y = p, split(ch[0][p], v, x, ch[0][y]), pushup(p);
    }

    inline int merge(int x, int y) {
        if (!x || !y) return x + y;
        if (pri[x] > pri[y]) return ch[1][x] = merge(ch[1][x], y), pushup(x), x;
        else return ch[0][y] = merge(x, ch[0][y]), pushup(y), y;
    }

    inline void insert(int v) { split(rt, v, a, b), rt = merge(a, merge(Newnode(v), b)); }

    inline void erase(int v) { split(rt, v, a, c), split(a, v - 1, a, b), b = merge(ch[0][b], ch[1][b]), rt = merge(a, merge(b, c)); }

    inline void build(int l, int r) { for (rg int i = l; i <= r; ++i) insert(A[i]); }

    inline int kth(int p, int k) {
        if (siz[ch[0][p]] + 1 > k) return kth(ch[0][p], k);
        if (siz[ch[0][p]] + 1 == k) return val[p];
        if (siz[ch[0][p]] + 1 < k) return kth(ch[1][p], k - siz[ch[0][p]] - 1);
    }

    inline int pre(int v) { split(rt, v - 1, a, b), c = a != 0 ? kth(a, siz[a]) : -INF, rt = merge(a, b); return c; }

    inline int nxt(int v) { split(rt, v, a, b), c = b != 0 ? kth(b, 1) : INF, rt = merge(a, b); return c; }

    inline int rank(int v) { split(rt, v - 1, a, b), c = siz[a] + 1, rt = merge(a, b); return c; }
} t[_ << 2];

inline int lc(int p) { return p << 1; }

inline int rc(int p) { return p << 1 | 1; }

inline void build(int p = 1, int l = 1, int r = n) {
    t[p].build(l, r);
    if (l == r) return ;
    int mid = (l + r) >> 1;
    build(lc(p), l, mid), build(rc(p), mid + 1, r);
}

inline void update(int x, int v, int p = 1, int l = 1, int r = n) {
    t[p].erase(A[x]), t[p].insert(v);
    if (l == r) return ;
    int mid = (l + r) >> 1;
    if (x <= mid) update(x, v, lc(p), l, mid);
    else update(x, v, rc(p), mid + 1, r);
}

inline int rank(int ql, int qr, int v, int p = 1, int l = 1, int r = n) {
    if (ql <= l && r <= qr) return t[p].rank(v) - 1;
    int mid = (l + r) >> 1, res = 0;
    if (ql <= mid) res += rank(ql, qr, v, lc(p), l, mid);
    if (qr > mid) res += rank(ql, qr, v, rc(p), mid + 1, r);
    return res;
}

inline int kth(int ql, int qr, int k) {
    int l = 0, r = 100000000, res;
    while (l <= r) {
    int mid = (l + r) >> 1;
    if (rank(ql, qr, mid) + 1 <= k) res = mid, l = mid + 1; else r = mid - 1;
    }
    return res;
}

inline int pre(int ql, int qr, int v, int p = 1, int l = 1, int r = n) {
    if (ql <= l && r <= qr) return t[p].pre(v);
    int mid = (l + r) >> 1, res = -INF;
    if (ql <= mid) res = max(res, pre(ql, qr, v, lc(p), l, mid));
    if (qr > mid) res = max(res, pre(ql, qr, v, rc(p), mid + 1, r));
    return res;
}

inline int nxt(int ql, int qr, int v, int p = 1, int l = 1, int r = n) {
    if (ql <= l && r <= qr) return t[p].nxt(v);
    int mid = (l + r) >> 1, res = INF;
    if (ql <= mid) res = min(res, nxt(ql, qr, v, lc(p), l, mid));
    if (qr > mid) res = min(res, nxt(ql, qr, v, rc(p), mid + 1, r));
    return res;
}

int main() {
#ifndef ONLINE_JUDGE
    file("cpp");
#endif
    read(n), read(m);
    for (rg int i = 1; i <= n; ++i) read(A[i]);
    build();
    for (rg int opt, l, r, k, x; m--; ) {
        read(opt);
        if (opt == 1) read(l), read(r), read(k), printf("%d\n", rank(l, r, k) + 1);
        if (opt == 2) read(l), read(r), read(k), printf("%d\n", kth(l, r, k));
        if (opt == 3) read(x), read(k), update(x, k), A[x] = k;
        if (opt == 4) read(l), read(r), read(k), printf("%d\n", pre(l, r, k));
        if (opt == 5) read(l), read(r), read(k), printf("%d\n", nxt(l, r, k));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zsbzsb/p/12232249.html

时间: 2024-08-01 16:11:55

「luogu3380」【模板】二逼平衡树(树套树)的相关文章

bzoj 3196/ Tyvj 1730 二逼平衡树 (线段树套平衡树)

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,

BZOJ3196 二逼平衡树 ZKW线段树套vector(滑稽)

我实在是不想再打一遍树状数组套替罪羊树了... 然后在普通平衡树瞎逛的时候找到了以前看过vector题解 于是我想:为啥不把平衡树换成vector呢??? 然后我又去学了一下ZKW线段树 就用ZKW线段树套vector水过啦!!! 每个ZKW线段树的节点保存一个vector 操作1在分出的vector上查询比它小的数有多少个然后相加再加1 操作2二分再上操作1 操作3修改需要修改的节点的vector 操作4在分出vector上查询前驱取最大 操作5与操作4同理 luogu主站5772ms上卡过,

BZOJ3196 二逼平衡树 【线段树套平衡树】

题目 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱定义为小于x,且最大的数) 5.查询k在区间内的后继(后继定义为大于x,且最小的数) 输入格式 第一行两个数 n,m 表示长度为n的有序序列和m个操作 第二行有n个数,表示有序序列 下面有m行,opt表示操作标号 若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名 若

3196. 二逼平衡树【线段树套splay】

Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱定义为小于x,且最大的数) 5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,m 表示长度为n的有序序列和m个操作 第二行有n个数,表示有序序列 下面有m行,opt表示操作标号 若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间

洛谷 P3380 【模板】二逼平衡树(树套树)

洛谷 P3380 [模板]二逼平衡树(树套树) 线段树套treap: 就是线段树每个节点放一个treap.建树复杂度应该是$n log n$,操作1,3,4,5的复杂度是$(log n)^2$,操作2的复杂度是$(log n)^3$. 操作3:找到线段树的对应叶子节点后找到要删除的值,在回溯的时候更新线段树相关的每一个节点(在treap中去掉要删除的值,再加入要加入的值) 操作1:将操作转化为统计(这个区间[l,r]内小于x的数的个数)+1.那么通过线段树将区间分解,然后对分解出的每一个区间对应

[BZOJ 3196] 二逼平衡树 树状数组套主席树

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3357  Solved: 1326[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为

BZOJ3196二逼平衡树【树套树】

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3776  Solved: 1483 Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n

BZOJ 3196: Tyvj 1730 二逼平衡树( 树套树 )

这道题做法应该很多吧.... 我用了线段树套treap.... -------------------------------------------------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #include<ios

洛谷3380 二逼平衡树(树套树)

题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数值 查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647) 查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647) 注意上面两条要求和tyvj或者bzoj不一样,请注意 输入输出格式 输入格式: 第一行两个数 n,m 表示长度为n的有序序列和m个操作 第二行有n个数,