二逼平衡树(树套树)
二逼平衡树是[平衡树三题]里的最后一题,或许是因为比较恶心,所以叫它二逼吧(你看文艺平衡树多好听(●ˇ∀ˇ●)),其实想想还是比较好写的。。。还有,我管它叫Segment Treap
(●’◡’●)
目录
题目
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)
思路
- 由于这些操作都是查排名之类的,首先会想到核心内容必定是由平衡树来完成;
- 再看题,都是区间的操作,所以想到外层应该是线段树,其实正解即如此!
- 大体上,就是先是线段树维护每个区间,之后线段树每个节点都是一个treap来维护(用treap主要是splay 常数太大,容易TLE)。这里要注意,虽然每个线段树节点都是treap,但不需要每个treap都开很大的节点数,实际上treap总结点数只需
2nlogn
个; - 操作1、3、4、5都很好写,就是先像普通的线段树一样,递归的找到操作区间的子区间,之后对该区间对应的treap操作即可;
- 难点主要是操作2,其实做法是二分答案,二分一个值然后看在查询区间里的排名。
代码
强烈推荐使用class封装
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int MAXN=100005,INF=~0U>>1,MAX=int(1e8),MIN=-MAX;
int cnt,n,m;
int tmp[MAXN];
struct node{
int s[2],fa,w,val,sz,cnt;
}d[1500005];
class treap{
private:
int sz,rt;
void sgl_upd(int u){
d[u].sz=d[u].cnt+d[d[u].s[0]].sz+d[d[u].s[1]].sz;
}
void path_upd(int u){
while(u){
sgl_upd(u);
u=d[u].fa;
}
}
void rot(int u){
int ufa=d[u].fa;
bool lr= d[ufa].s[1]==u;
d[u].fa=d[ufa].fa;
if(d[ufa].fa)
d[d[ufa].fa].s[d[d[ufa].fa].s[1]==ufa]=u;
else rt=u;
d[ufa].s[lr]=d[u].s[lr^1];
d[d[ufa].s[lr]].fa=ufa;
d[u].s[lr^1]=ufa;
d[ufa].fa=u;