Tyvj1728 普通平衡树(splay | 非旋treap | 树状数组 | 宗法树 | vector)
题目
Description
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 1. 插入x数 2. 删除x数(若有多个相同的数,因只删除一个) 3. 查询x数的排名(若有多个相同的数,因输出最小的排名) 4. 查询排名为x的数 5. 求x的前驱(前驱定义为小于x,且最大的数) 6. 求x的后继(后继定义为大于x,且最小的数)
Input
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
Output
对于操作3,4,5,6每行输出一个数,表示对应答案
Sample Input
1 | 10 |
Sample Output
1 | 106465 |
HINT
n的数据范围:n<=100000- 每个数的数据范围:
[-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
查询排名为x的数 search
从根结点开始,若左儿子size大于等于x,向左走,若加上该结点size小于x向右走,若为该结点则返回
前驱 predeccessor
插入x,splay到根结点,返回左子树最大值,删除x
后继 successor
插入x,splay到根结点,返回右子树最小值,删除x
完整代码 code
1 |
|
非旋树堆 FHQ Treap
只有两个操作,能够实现可持久化
分离 split
按键值将tree分为x,y两棵树(也可按权值)
合并 merge
按权值将两棵树合并为一棵树
插入 insert
将原树分为x,y两棵树,将新节点看成一棵树与x合并,再与y合并
删除 erase
首先我们把树分为x和z两部分
那么x树中的最大权值为a
再把x分为x和y两部分。
此时x中的最大权值为a-1,且权值为a的节点一定是y的根节点。
然后我们可以无视y的根节点,直接把y的左右孩子合并起来,这样就成功的删除了根节点,
最后再把x,y,z合并起来就好
其他操作见代码
完整代码 code
1 |
|
宗法树
这是一个非常毒瘤的数据结构,因其删除操作特点而得名
它的特点有: 1. 叶节点存的是数,非叶节点存的是左右两棵子树的最大值 2. 非叶节点左子树的值都比右子树的值小 3. 非叶节点必有左右两棵子树
插入 insert
若无结点则新建,若有,根据性质找到一个叶结点,新建两个子结点,按顺序排这两个数,更新原结点
删除 erase
找到该点,用另一子结点将父亲覆盖
其他操作见代码
完整代码 code
洛谷最新数据会被卡掉一个点
1 |
|
2018.7.24更新
vector解法
突然想起来可以用vector过,虽然非常暴力但真的过了
代码 code
比指针版的非旋Treap还快(可能是我写的有毛病)
1 |
|
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
1 |
|