洛谷-P3369-普通平衡树(Treap)

题目传送门

标题说平衡树,那么应该AVL,红黑树都能过,但是这次做这题主要是学习Treap,所以花了几天搞出了这题。其他方法以后再说吧

  • Treap(带旋转)
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 100010;
    const int INF = 0x3f3f3f3f;
    struct Treap {
        int son[2];
        int val, rand;
        int cnt, size;
    } node[MAXN];
    int root, tot;
    void update(int i) {
        int ls = node[i].son[0];
        int rs = node[i].son[1];
        node[i].size = node[ls].size + node[rs].size + node[i].cnt;
    }
    void rotate(int& r, int index) {
        int k = node[r].son[index ^ 1];
        node[r].son[index ^ 1] = node[k].son[index];
        node[k].son[index] = r;
        update(r); update(k);
        r = k;
    }
    void insert(int& r, int v) {
        if (r == 0) {
            r = ++ tot;
            node[r].val = v;
            node[r].rand = rand();
            node[r].size = node[r].cnt = 1;
            return;
        }
        if (node[r].val == v) {
            node[r].size ++;
            node[r].cnt ++;
            return;
        }
        int index = v > node[r].val;
        insert(node[r].son[index], v);
        if (node[node[r].son[index]].rand < node[r].rand) rotate(r, index ^ 1);
        update(r);
    }
    void erase(int& r, int v) {
        if (r == 0) return;
        if (v == node[r].val) {
            if (node[r].cnt > 1) {
                node[r].cnt --;
                node[r].size --;
                return;
            }
            if (node[r].son[0] == 0 || node[r].son[1] == 0) {
                r = node[r].son[0] + node[r].son[1];
            } else {
                int ls = node[r].son[0];
                int rs = node[r].son[1];
                int index = node[ls].rand < node[rs].rand;
                rotate(r, index);
                erase(node[r].son[index], v);
            }
        } else {
            int index = v > node[r].val;
            erase(node[r].son[index], v);
        }
        update(r);
    }
    int get_rank(int r, int v) {
        int size = node[node[r].son[0]].size;
        int cnt = node[r].cnt;
        if (node[r].val == v) return size + 1;
        if (node[r].val > v) return get_rank(node[r].son[0], v);
        if (node[r].val < v) return size + cnt + get_rank(node[r].son[1], v);
    }
    int get_val(int r, int rk) {
        int size = node[node[r].son[0]].size;
        int cnt = node[r].cnt;
        if (rk < size + 1) return get_val(node[r].son[0], rk);
        if (rk >= size + 1 && rk <= size + cnt) return node[r].val;
        if (rk > size + cnt) return get_val(node[r].son[1], rk - size - cnt);
    }
    int get_pre(int r, int v) {
        if (r == 0) return -INF;
        if (node[r].val >= v) return get_pre(node[r].son[0], v);
        else return max(node[r].val, get_pre(node[r].son[1], v));
    }
    int get_suc(int r, int v) {
        if (r == 0) return INF;
        if (node[r].val <= v) return get_suc(node[r].son[1], v);
        else return min(node[r].val, get_suc(node[r].son[0], v));
    }
    int main() {
        srand(time(NULL));
        int n; scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            int opt, x;
            scanf("%d%d", &opt, &x);
            if (opt == 1) insert(root, x);
            if (opt == 2) erase(root, x);
            if (opt == 3) printf("%d\n", get_rank(root, x)); 
            if (opt == 4) printf("%d\n", get_val(root, x));
            if (opt == 5) printf("%d\n", get_pre(root, x));
            if (opt == 6) printf("%d\n", get_suc(root, x));
        }
        return 0;
    }

    学到的第二个用随机数的算法,所以这个Treap的高度也是有的随机的。但是起码不太可能退化成链状。

  • Treap(无旋转)
    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 100010;
    const int INF = 0x3f3f3f3f;
    struct Treap {
        int ls, rs;
        int val, rand;
        int size;
    } node[MAXN];
    int tot, root;
    int add_node(int v) {
        int i = ++tot;
        node[i].val = v;
        node[i].rand = rand();
        node[i].ls = node[i].rs = 0;
        node[i].size = 1;
        return i;
    }
    void split(int rt, int& a, int& b, int v) { 
        if (rt == 0) {
            a = b = 0;
            return;
        }
        if (node[rt].val <= v) {
            a = rt;
            split(node[rt].rs, node[a].rs, b, v);
        } else {
            b = rt;
            split(node[rt].ls, a, node[b].ls, v);
        }
        node[rt].size = node[node[rt].ls].size + node[node[rt].rs].size + 1;
    }
    void merge(int& rt, int a, int b) {
        if (a == 0 || b == 0) {
            rt = a + b;
            return;
        }
        if (node[a].rand < node[b].rand) {
            rt = a;
            merge(node[rt].rs, node[a].rs, b);
        } else {
            rt = b;
            merge(node[rt].ls, a, node[b].ls);
        }
        node[rt].size = node[node[rt].ls].size + node[node[rt].rs].size + 1;
    }
    void insert(int& rt, int v) {
        int x = 0, y = 0;
        split(rt, x, y, v);
        merge(x, x, add_node(v));
        merge(rt, x, y);
    }
    void erase(int&rt, int v) {
        int x = 0, y = 0, z = 0;
        split(rt, x, z, v);
        split(x, x, y, v - 1);
        merge(y, node[y].ls, node[y].rs);
        merge(x, x, y);
        merge(rt, x, z);
    }
    int get_rank(int rt, int v) {
        int x = 0, y = 0;
        split(rt, x, y, v - 1);
        int rk = node[x].size + 1;
        merge(rt, x, y);
        return rk;
    }
    int get_val(int rt, int rk) {
        int size = node[node[rt].ls].size;
        if (rk < size + 1) return get_val(node[rt].ls, rk);
        if (rk == size + 1) return node[rt].val;
        if (rk > size + 1) return get_val(node[rt].rs, rk - size - 1);
    }
    int get_pre(int rt, int v) {
        int x = 0, y = 0;
        split(rt, x, y, v - 1);
        int pre = get_val(x, node[x].size);
        merge(rt, x, y);
        return pre;
    }
    int get_suc(int rt, int v) {
        int x = 0, y = 0;
        split(rt, x, y, v);
        int suc = get_val(y, 1);
        merge(rt, x, y);
        return suc;
    }
    int main() {
        srand(time(NULL));
        int n; scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            int opt, x;
            scanf("%d%d", &opt, &x);
            if (opt == 1) insert(root, x);
            if (opt == 2) erase(root, x);
            if (opt == 3) printf("%d\n", get_rank(root, x));
            if (opt == 4) printf("%d\n", get_val(root, x));
            if (opt == 5) printf("%d\n", get_pre(root, x));
            if (opt == 6) printf("%d\n", get_suc(root, x));
        }
        return 0;
    }

    昨天看懂了带旋转的Treap,今天来写无旋转的Treap,感觉确实是比带旋转的好敲而且好懂,关键就是理解split()和merge()两个核心函数。

posted @ 2019-11-04 16:21  Angel&Demon  阅读(...)  评论(... 编辑 收藏