二逼平衡树
题目要求
题目要求就是写一种数据结构,对于一个长度为$N(N≤50000)$的序列快速对以下的$M(M≤50000)$个操作快速的询问。
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)
题解
单纯的平衡树对于区间操作肯定会超时。所以我们可以用线段树套平衡树来实现。但是如果对于每个线段树结点都存一颗平衡树的所有信息,由于每个结点最坏需要$n$个结点来维护全序列,所以空间接受不了。于是我们对于平衡树和线段树分开实现,线段树每个节点记录一下对于平衡树的根,注意在平衡树中涉及旋转操作,所以在线段树中递归查询到某一层的时候,在平衡树中查询要把线段树编号也传进去。
下面给出每个操作实现的思路(Splay)
1.查询k在区间内的排名
对于跟普通线段树中一样的递归查询,当且仅当当前区间完全被包含在查询区间中到Splay中查询,$O(nlog^2n)$
2.查询区间内排名为k的值
这个操作不好实现,所以我们采用二分答案,二分这个第k的值,然后在区间中查询这个数的rank,然后对中点进行调整,$O(nlog^3n)$
3.修改某一位值上的数值
对于线段树递归修改,每一层的Splay都考虑这个点的修改,可以删除原来的点再插入现在的点。同时对于修改位置与当前线段树结点维护区间中点的关系,递归修改左儿子或右儿子。$O(nlog^2n)$
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
对于跟普通线段树中一样的递归查询,当且仅当当前区间完全被包含在查询区间中到Splay中查询,$O(nlog^2n)$
5.查询k在区间内的后继(后继定义为大于x,且最小的数)
对于跟普通线段树中一样的递归查询,当且仅当当前区间完全被包含在查询区间中到Splay中查询,$O(nlog^2n)$
所以还是很好实现的么。但是我原来写的Splay完美应用了Splay插入后该节点旋转到根的性质,查询操作通过插入删除来实现,导致了空间和时间的大量浪费。
改用另外一种直接在Splay中遍历查找的方法,时空复杂度都基本降低至了原来的$\frac 1 2$。
Code:
version1(slow)
|
|
version2
|
|
(这代码270行,占了这篇博客的大部分篇幅呢。。。)