Tyvj1728 普通平衡树(splay | 非旋treap | 树状数组 | 宗法树 | vector)

题目

Description

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入x数
  2. 删除x数(若有多个相同的数,因只删除一个)
  3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
  4. 查询排名为x的数
  5. 求x的前驱(前驱定义为小于x,且最大的数)
  6. 求x的后继(后继定义为大于x,且最小的数)

Input

第一行为n,表示操作的个数,下面n行每行有两个数optxopt表示操作的序号(1<=opt<=6)

Output

对于操作3,4,5,6每行输出一个数,表示对应答案

Sample Input

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

Sample Output

106465
84185
492737

HINT

  1. n的数据范围:n<=100000
  2. 每个数的数据范围:[-2e9,2e9]

题解

这道题是平衡树的模板题,是平衡树都能用,我用了三种方法写

BST二叉排序树或平衡树具有以下性质:

  1. 若左子树不为空,则左子树所有结点的值都小于等于它根结点的值
  2. 若右子树不为空,则右子树所有结点的值都大于等于它根结点的值
  3. 左右子树也分别为二叉排序树

伸展树 Splay

可以通过旋转来更换结点位置

插入 insert

从根结点开始,如果比x大则向左,如果比x小就向右,如果相等则增加其count的值,没有就新建一个结点

删除 erase

从根结点开始,找到该点,splay到根结点,若count大于1则自减,其余情况:

  1. 没有子结点,删除根结点,令root=nullptr
  2. 有左或右儿子,令root为该儿子,删除该结点
  3. 两个儿子都有,将右子树最小结点splay到根的右儿子,令根的左儿子成为右儿子的左儿子,删除该结点
查询x的排名 rank

从根结点开始,找到该点,splay到根结点,返回左子树size加上1

从根结点开始,若左儿子size大于等于x,向左走,若加上该结点size小于x向右走,若为该结点则返回

前驱 predeccessor

插入xsplay到根结点,返回左子树最大值,删除x

后继 successor

插入x,splay到根结点,返回右子树最小值,删除x

完整代码 code
#include<cstdio>
using namespace std;
int f(-1);
inline char GetCharacter() {
    static char buf[2000000], *p1 = buf, *p2 = buf;
    return (p1 == p2) &&
           (p2 = (p1 = buf) + fread(buf, 1, 2000000, stdin), p1 == p2) ? 
           EOF : *p1++;
}
#define IS_DIGIT(c) (c >= '0' && c <= '9')
inline void Read(int &x) {
    f = 1, x = 0;
    static char c = GetCharacter();
    while (!IS_DIGIT(c)) {
        if (c == '-') f = -1;
        c = GetCharacter();
    }
    while (IS_DIGIT(c)) x = x * 10 + c - '0', c = GetCharacter();
    x *= f;
}
#undef IS_DIGIT
class splay_tree {
    private:
        struct node {
            int value, size, count;
            node *left, *right, *father;
            node(const int v = 0) {
                value = v, size = 1, count = 1;
                left = right = father = NULL;
            }
        }*root;
        inline void update(register node *x) {
            if (x) x->size = x->count + 
             (x->left ? x->left->size : 0) + 
             (x->right ? x->right->size : 0);
        }
        inline void left_rotate(register node *x) {
            register node *y = x->father;
            y->right = x->left;
            if (x->left) x->left->father = y;
            x->father = y->father;
            if (y->father) {
                if (y == y->father->left) y->father->left = x;
                else y->father->right = x;
            }
            x->left = y;
            y->father = x;
            update(y);
            update(x);
        }
        inline void right_rotate(register node *x) {
            register node *y = x->father;
            y->left = x->right;
            if (x->right) x->right->father = y;
            x->father = y->father;
            if (y->father) {
                if (y == y->father->right) y->father->right = x;
                else y->father->left = x;
            }
            x->right = y;
            y->father = x;
            update(y);
            update(x);
        }
        inline void splay(register node *x,const node *target=NULL) {
            register node *y;
            while (x->father != target) {
                y = x->father;
                if (x == y->left) {
                    if (y->father != target && y == y->father->left) right_rotate(y);
                    right_rotate(x);
                } else {
                    if (y->father != target && y == y->father->right) left_rotate(y);
                    left_rotate(x);
                }
            }
            if (!target) root = x;
        }
        inline node *find(const int &key) {
            register node *x = root;
            while (1) {
                if (!x) break;
                else if (x->value == key)break;
                if (x->value < key) x = x->right;
                else if (x->value > key) x = x->left;
            }
            if (x) splay(x);
            return x;
        }
        inline node *subtree_maximum(register node *x) {
            if (!x) return NULL;
            while (x->right) x = x->right;
            return x;
        }
        inline node *subtree_minimum(register node *x) {
            if (!x) return NULL;
            while (x->left) x = x->left;
            return x;
        }
    public:
        inline void insert(const int &key) {
            register node *x = root, *y = NULL;
            while (1) {
                if (!x) break;
                else if (x->value == key) break;
                y = x;
                if (x->value < key) x = x->right;
                else if (x->value > key) x = x->left;
            }
            if (x) ++x->count, ++x->size;
            else {
                x = new node(key);
                x->father = y;
                if (y) {
                    if (x->value > y->value) y->right = x;
                    else y->left = x;
                }
            }
            if (!y) root = x;
            update(y);
            splay(x);
        }
        inline void erase(const int &key) {
            register node *x = find(key);
            if (x) {
                if (x->count > 1) --x->count, --x->size;
                else {
                    if (!x->left && !x->right) {
                        delete root;
                        root = NULL;
                        return;
                    } else if (!x->left) {
                        root = x->right;
                        root->father = NULL;
                        delete x;
                        return;
                    } else if (!x->right) {
                        root = x->left;
                        root->father = NULL;
                        delete x;
                        return;
                    } else {
                        register node *y = subtree_minimum(x->right);
                        splay(y, x);
                        y->left = x->left, x->left->father = y;
                        y->father = NULL;
                        delete x;
                        root = y;
                        update(y);
                    }
                }
            }
        }
        inline int rank(const int &key) {
            register node *x = find(key);
            if (x) return (((x->left ? x->left->size : 0 ) + 1));
            else return 0;
        }
        inline int search(register int rnk) {
            register node *x = root;
            while(1) {
                if (x->left) {
                    if (x->left->size < rnk && x->left->size + x->count >= rnk) break;
                    else if (x->left->size >= rnk) x = x->left;
                    else rnk -= x->left->size + x->count, x = x->right;
                } else {
                    if (x->count >= rnk) break;
                    else rnk -= x->count, x = x->right;
                }
            }
            return x->value;
        }
        inline int predeccessor(const int &key) {
            insert(key);
            register node *ret = subtree_maximum(root->left);
            splay(ret);
            erase(key);
            return ret->value;
        }
        inline int successor(const int &key) {
            insert(key);
            register node *ret = subtree_minimum(root->right);
            splay(ret);
            erase(key);
            return ret->value;
        }
};
int n, opt, x;
splay_tree tree;
int main(int argc, char **argv) {
    scanf("%d", &n);
    while (n--) {
        scanf("%d %d", &opt, &x);
        switch(opt) {
            case 1: {
                tree.insert(x);
                break;
            }
            case 2: {
                tree.erase(x);
                break;
            }
            case 3: {
                printf("%d
", tree.rank(x));
                break;
            }
            case 4: {
                printf("%d
", tree.search(x));
                break;
            }
            case 5: {
                printf("%d
", tree.predeccessor(x));
                break;
            }
            case 6: {
                printf("%d
", tree.successor(x));
                break;
            }
        }
    }
    return 0;
}

非旋树堆 FHQ Treap

只有两个操作,能够实现可持久化

分离 split

按键值将tree分为xy两棵树(也可按权值)

合并 merge

按权值将两棵树合并为一棵树

插入 insert

将原树分为xy两棵树,将新节点看成一棵树与x合并,再与y合并

删除 erase

首先我们把树分为xz两部分

那么x树中的最大权值为a

再把x分为xy两部分。

此时x中的最大权值为a-1,且权值为a的节点一定是y的根节点。

然后我们可以无视y的根节点,直接把y的左右孩子合并起来,这样就成功的删除了根节点,

最后再把xyz合并起来就好

其他操作见代码
完整代码 code
#include<cstdio>
#include<cstdlib>
using namespace std;
int f(-1);
inline char GetCharacter() {
    static char buf[2000000], *p1 = buf, *p2 = buf;
    return (p1 == p2) &&
           (p2 = (p1 = buf) + fread(buf, 1, 2000000, stdin), p1 == p2) ? 
           EOF : *p1++;
}
#define IS_DIGIT(c) (c >= '0' && c <= '9')
inline void Read(int &x) {
    f = 1, x = 0;
    static char c = GetCharacter();
    while (!IS_DIGIT(c)) {
        if (c == '-') f = -1;
        c = GetCharacter();
    }
    while (IS_DIGIT(c)) x = x * 10 + c - '0', c = GetCharacter();
    x *= f;
}
#undef IS_DIGIT
class fhq_treap {
    private:
        struct node {
            node *left, *right;
            int value, prize, size;
            node(const int v = 0) {
                left = right = NULL;
                value = v, prize = rand(), size = 1;
            }
        }*root, *x, *y, *z;
        int size;
        inline void update(register node *x) {
            x->size = 1 + (x->left ? x->left->size : 0) + 
                    (x->right ? x->right->size : 0);
        }
        inline node *new_node(const int v = 0) {
            ++size;
            register node *tmp = new node(v);
            return tmp;
        }
        inline node *merge(register node *x, register node *y) {
            if (!x) return y;
            if (!y) return x;
            if (x->prize < y->prize) {
                x->right = merge(x->right, y);
                update(x);
                return x;
            } else {
                y->left = merge(x, y->left);
                update(y);
                return y;
            }
        }
        inline void split(register node *now,const int k,register node *&x,
                                                     register node *&y) {
            if (!now) x = y = NULL;
            else {
                if (now->value <= k) x = now, split(now->right, k, now->right, y);
                else y = now, split(now->left, k, x, now->left);
                update(now);
            }
        }
        inline node *kth(register node *now, register int k) {
            while(1) {
                if (now->left != NULL) {
                    if (k <= now->left->size) now = now -> left;
                    else if (k == (now->left ? now->left->size : 0) + 1) return now;
                    else k -= (now->left ? now->left->size : 0) + 1, now = now->right;
                } else if (k == (now->left ? now->left->size : 0) + 1) return now;
                else k -= (now->left ? now->left->size : 0) + 1, now = now->right;
            }
        }
    public:
        fhq_treap() {
            size = 0;
            root = NULL;
        }
        inline void insert(const int &key) {
            split(root, key, x, y);
            root = merge(merge(x, new_node(key)), y);
        }
        inline void erase(const int &key) {
            split(root, key, x, z);
            split(x, key - 1, x, y);
            y = merge(y->left, y->right);
            root = merge(merge(x, y), z);
        }
        inline int rank(const int key) {
            split(root, key - 1, x, y);
            register int ret = (x ? x->size : 0) + 1;
            root = merge(x, y);
            return ret;
        }
        inline int search(const int &rnk) {
            return kth(root, rnk)->value;
        }
        inline int predeccessor(const int &key) {
            split(root, key - 1, x, y);
            register int ret(kth(x, x->size)->value);
            root = merge(x, y);
            return ret;
        }
        inline int successor(const int &key) {
            split(root, key, x, y);
            register int ret(kth(y, 1)->value);
            root = merge(x, y);
            return ret;
        }
};
int n, opt, x;
fhq_treap tree;
int main(int argc, char **argv) {
    Read(n);
    while(n--) {
        scanf("%d %d", &opt, &x);
        Read(opt), Read(x);
        switch(opt) {
            case 1: {
                tree.insert(x);
                break;
            }
            case 2: {
                tree.erase(x);
                break;
            }
            case 3: {
                printf("%d
", tree.rank(x));
                break;
            }
            case 4: {
                printf("%d
", tree.search(x));
                break;
            }
            case 5: {
                printf("%d
", tree.predeccessor(x));
                break;
            }
            case 6: {
                printf("%d
", tree.successor(x));
                break;
            }
        }
    }
    return 0;
}

宗法树 Patriarchal Tree

这是一个非常毒瘤的数据结构,因其删除操作特点而得名

它的特点有:

  1. 叶节点存的是数,非叶节点存的是左右两棵子树的最大值
  2. 非叶节点左子树的值都比右子树的值小
  3. 非叶节点必有左右两棵子树
插入 insert

若无结点则新建,若有,根据性质找到一个叶结点,新建两个子结点,按顺序排这两个数,更新原结点

删除 erase

找到该点,用另一子结点将父亲覆盖

其他操作见代码
完整代码 code

洛谷最新数据会被卡掉一个点

#include<cstdio>
#include<algorithm>
using namespace std;
int f(-1);
inline char GetCharacter() {
    static char buf[2000000], *p1 = buf, *p2 = buf;
    return (p1 == p2) &&
           (p2 = (p1 = buf) + fread(buf, 1, 2000000, stdin), p1 == p2) ? 
           EOF : *p1++;
}
#define IS_DIGIT(c) (c >= '0' && c <= '9')
inline void Read(int &x) {
    f = 1, x = 0;
    static char c = GetCharacter();
    while (!IS_DIGIT(c)) {
        if (c == '-') f = -1;
        c = GetCharacter();
    }
    while (IS_DIGIT(c)) x = x * 10 + c - '0', c = GetCharacter();
    x *= f;
}
#undef IS_DIGIT
class patriarchal_tree {
    private:
        struct node {
            int val, size;
            node *left, *right, *father;
            node(const int v = 0) {
                val = v, size = 1, left = right = father = NULL;
            }
        }*root;
        inline void update(register node *x){
            if (x && x->left) x->val = max(x->left->val, x->right->val), 
                        x->size = x->left->size + x->right->size;
        }
        inline bool isleaf(const node *x){
            return x->left == NULL;
        }
        inline void Insert(const int &v,register node *s){
            if (!s) {
                s = new node(v);
                root = s;
                return;
            }else if (!s->left) {
                s->left = new node (min(s->val , v));
                s->right = new node(max(s->val , v));
                s->left->father = s->right->father = s;
                update(s);
                return;
            }
            if (v > s->left->val) {
                Insert(v, s->right);
            } else Insert(v, s->left);
            update(s);
        }
        inline void Erase(const int &v,register node *s){
            if (isleaf(s)) {
                register node *p = s->father, *t;
                if (!p) {
                    root = NULL;
                    delete s;
                }
                else if (p->right == s) {
                    t = p->left;
                    if (p->left->left) {
                        p->left->left->father = p->left->right->father = p;
                    }
                    p->val = p->left->val, p->size = p->left->size;
                    p->left = t->left, p->right = t->right;
                    delete t;
                    delete s;
                }
                else {
                    t = p->right;
                    if (p->right->left) {
                        p->right->left->father = p->right->right->father = p;
                    }
                    p->val = p->right->val, p->size = p->right->size;
                    p->right = t->right, p->left = t->left;
                    delete t;
                    delete s;
                }
                return;
            }
            if (v > s->left->val) {
                Erase(v, s->right);
            }else Erase(v, s->left);
            update(s);
        }
        inline int Rank(const int &v,const node *s){
            if (isleaf(s)) {
                if (s->val < v) return 2;
                return 1;
            }
            if(v > s->left->val) return Rank(v, s->right) + s->left->size;
            else return Rank(v, s->left);
        }
        inline int Search(register int v,const node *s){
            if (isleaf(s)) return s->val;
            else if (v > s->left->size) return Search(v - s->left->size, s->right);
            else return Search(v, s->left);
        }
    public:
        patriarchal_tree(){
            root = NULL;
        }
        inline void insert(const int &v){
            Insert(v, root);
        }
        inline void erase(const int &v){
            Erase(v, root);
        }
        inline int rank(const int &v){
            return Rank(v, root);
        }
        inline int search(const int &v){
            return Search(v, root);
        }
        inline int predecessor(const int &v){ 
            return Search(Rank(v, root) - 1, root);
        }
        inline int successor(const int &v){
            return Search(Rank(v + 1, root), root);
        }
};
patriarchal_tree T;
int n, opt, x;
int main(int argc, char **argv){
    Read(n);
    while (n--) {
        Read(opt), Read(x);
        if (opt == 1) T.insert(x);
        else if (opt == 2) T.erase(x);
        else if (opt == 3) printf("%d
", T.rank(x));
        else if (opt == 4) printf("%d
", T.search(x));
        else if (opt == 5) printf("%d
", T.predecessor(x));
        else if (opt == 6) printf("%d
", T.successor(x));
    }
    return 0;
}


2018.7.24更新

vector解法

突然想起来可以用vector过,虽然非常暴力但真的过了

代码 code

比指针版的非旋Treap还快(可能是我写的有毛病)

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int f(-1);
inline char GetCharacter() {
    static char buf[2000000], *p1 = buf, *p2 = buf;
    return (p1 == p2) &&
           (p2 = (p1 = buf) + fread(buf, 1, 2000000, stdin), p1 == p2) ? 
           EOF : *p1++;
}
#define IS_DIGIT(c) (c >= '0' && c <= '9')
inline void Read(int &x) {
    f = 1, x = 0;
    static char c = GetCharacter();
    while (!IS_DIGIT(c)) {
        if (c == '-') f = -1;
        c = GetCharacter();
    }
    while (IS_DIGIT(c)) x = x * 10 + c - '0', c = GetCharacter();
    x *= f;
}
#undef IS_DIGIT
vector<int> v;
int n, opt, x;
int main(int argc, char **argv) {
    scanf("%d", &n);
    v.reserve(n + 5);
    while (n--) {
        scanf("%d %d", &opt, &x);
        switch(opt) {
            case 1:
                v.insert(upper_bound(v.begin(), v.end(), x), x);
                break;
            case 2:
                v.erase(lower_bound(v.begin(), v.end(), x));
                break;
            case 3:
                printf("%d
", lower_bound(v.begin(), v.end(), x) - v.begin() + 1);
                break;
            case 4:
                printf("%d
", v[x - 1]);
                break;
            case 5:
                printf("%d
", *--lower_bound(v.begin(), v.end(), x));
                break;
            case 6:
                printf("%d
", *upper_bound(v.begin(), v.end(), x));
        }
    }
    return 0;
}

2018.8.17更新

树状数组 fenwick tree

又一个令马制烯的方法,但对于平衡树的题树状数组估计也就只能干这个了,不过跑得飞快

离散化 Discretization

为了节省内存,我们在这里进行离散化,当然如果数据范围过大则必须进行离散化

当然离散化后就只能离线做了

离散化后排序去重,为了方便可以使用STL

修改 Add

树状数组基本操作,不再解释

求和 GetSum

树状数组基本操作,不再解释

插入 Insert

x离散化后的数作为下标,在树状数组对应的地方增加1

删除 Erase

x离散化后的数作为下标,在树状数组对应的地方减去1

查询排名 Rank

求当前树状数组中下标小于DISCRETE(x)的前缀和

查询排名为x的数 Search

这里是树状数组解法中最困难的一个,用到了倍增的思想

当我们把fenwick_tree[i]的下标加上1 << k后,

fenwick_tree[i + (1 << k)]的值就是我们增加的这一段区间元素的个数

就可以直接判断当前元素个数是否超过了rank,超过了就退回去,否则就保存

求x的前驱 Predecessor

找到比该数排名小1的数

求x得后继 Successor

与求前驱同理

完整代码 Code
#include<cstdio>
#include<algorithm>
using namespace std;
int f(-1);
inline char GetCharacter() {
    static char buf[2000000], *p1 = buf, *p2 = buf;
    return (p1 == p2) &&
           (p2 = (p1 = buf) + fread(buf, 1, 2000000, stdin), p1 == p2) ? 
           EOF : *p1++;
}
#define IS_DIGIT(c) (c >= '0' && c <= '9')
inline void Read(int &x) {
    f = 1, x = 0;
    static char c = GetCharacter();
    while (!IS_DIGIT(c)) {
        if (c == '-') f = -1;
        c = GetCharacter();
    }
    while (IS_DIGIT(c)) x = x * 10 + c - '0', c = GetCharacter();
    x *= f;
}
#undef IS_DIGIT
#define LOWBIT(i) ((i) & (-i))
int fenwick_tree[100010], tree_size;
int table[100010], number_quantity;
inline void Add(register int index, const int &delta) {
  while (index <= tree_size) {
    fenwick_tree[index] += delta;
    index += LOWBIT(index);
  }
}
inline int GetSum(register int index) {
  register int ret(0);
  while (index) {
    ret += fenwick_tree[index];
    index -= LOWBIT(index);
  }
  return ret;
}
inline void Discretization() {
  sort(table + 1, table + number_quantity + 1);
  tree_size = unique(table + 1, table + number_quantity + 1) - (table + 1);
}
#define DISCRETE(x) lower_bound(table + 1, table + tree_size + 1, x) - table
inline void Insert(const int &key) {
  Add(DISCRETE(key), 1);
}
inline void Erase(const int &key) {
  Add(DISCRETE(key), -1);
}
inline int Rank(const int &key) {
  return GetSum(DISCRETE(key) - 1) + 1;
}
inline int Search(register int rank) {
  register int ret(0);
  for (register int i(20); i >= 0; --i) {
    ret += 1 << i;
    if(ret >= tree_size || fenwick_tree[ret] >= rank) ret -= 1 << i;
    else rank -= fenwick_tree[ret];
  }
  return table[++ret];
}
inline int Predeccessor(const int &key) {
  return Search(GetSum(DISCRETE(key) - 1));
}
inline int Successor(const int &key) {
  return Search(GetSum(DISCRETE(key)) + 1);
}
#undef LOWBIT
int n, opt[100010], x[100010];
int main(int argc, char **argv) {
  Read(n);
  for (register int i(1); i <= n; ++i) {
    Read(opt[i]), Read(x[i]);
    if (opt[i] != 4) table[++number_quantity] = x[i];
  }
  Discretization();
  for (register int i(1); i <= n; ++i) {
    switch(opt[i]) {
      case 1: {
        Insert(x[i]);
        break;
      }
      case 2: {
        Erase(x[i]);
        break;
      }
      case 3: {
        printf("%d
", Rank(x[i]));
        break;
      }
      case 4: {
        printf("%d
", Search(x[i]));
        break;
      }
      case 5: {
        printf("%d
", Predeccessor(x[i]));
        break;
      }
      case 6: {
        printf("%d
", Successor(x[i]));
        break;
      }
    }
  }
  return 0;
}
原文地址:https://www.cnblogs.com/forth/p/9305086.html