这是一种很神奇的数据结构(似乎我每个数据结构都要这么说)
尽管可能是我的模板太丑了,速度实测出来似乎并不比splay快,不过要做到可持久化的话,
这种数据结构就是首选。
另外,这种数据结构相当好写。
简单地说,这种treap基于两种操作:
Merge(int x,int y)->将x的子树和y的子树合并起来,且满足 x的子树的最大值小于等于y子树的最小值,复杂度O(logN)
Split ( int x,int k ) ->将x的 最小的k个值/小于等于k的值 与另外的部分分离出来,复杂度O(logN)
首先是Merge操作:
因为已知x的子树最大值小于等于y子树的最小值,
很容易想到必定是y链接在x的右儿子节点,或者x链接在y的左儿子节点(详见代码)
为了使深度尽量小,我们使用随机数,来判断x为y的儿子,或者y为x的儿子
int Merge(int x,int y){
if(x==0) return y;
if(y==0) return x;
if(Tree[x].fix<Tree[y].fix){
Tree[x].ch[1]=Merge(Tree[x].ch[1],y);//将y连在x的右儿子节点
update(x);
return x;
}
else{
Tree[y].ch[0]=Merge(x,Tree[y].ch[0]);//将x连在y左儿子节点
update(y);
return y;
}
}
接下来是Split操作
(有两种不同的Split方式,根据题目,可以自行选用合适的一种)
首先我们要将这颗树分成两部分,
以下代码中first为前半部分,second为后半部分
pair<int,int> Split(int x,int k){
if(x==0) return make_pair(0,0);
pair<int,int> y;
if(Tree[x].key<=k){//将子树中值小于等于k的分离出来
y=Split(Tree[x].ch[1],k);//x此时必定在小于等于k的部分,但x的右儿子是否在这部分并不确定,所以递归操作到叶子节点为止
Tree[x].ch[1]=y.first;//将x的子树中与x在同一部分的相连接,
update(x);
y.first=x;
}
else{//与上面完全相反
y=Split(Tree[x].ch[0],k);
Tree[x].ch[0]=y.second;
update(x);
y.second=x;
}
return y;
}
第二种:
pair<int,int> Split(int x,int k){
if(x==0) return make_pair(0,0);
pair<int,int> y;
if(Tree[Tree[x].ch[0]].sum<k){//将子树最小的k个分离出来(如有相同可能会多于k个)
y=Split(Tree[x].ch[1],k);
Tree[x].ch[1]=y.first;
update(x);
y.first=x;
}
else{
y=Split(Tree[x].ch[0],k);
Tree[x].ch[0]=y.second;
update(x);
y.second=x;
}
return y;
}
现在,基于这两种操作,我们就可以写出来一个平衡树了(是不是很好写!!)
那么首先是插入操作:
void Insert(int val){
pair<int,int> x=Split(root,val);
Tree[++ncnt]=node(val);//初始化
Tree[ncnt].ch[0]=0;
Tree[ncnt].ch[1]=0;
root=Merge(Merge(x.first,ncnt),x.second);
}
是不是很简单?
然后是删除操作:
void Del(int val){
pair<int,int> x=Split(root,val);
pair<int,int> y=Split(x.first,val-1);
y.second=Merge(Tree[y.second].ch[0],Tree[y.second].ch[1]);
root=Merge(Merge(y.first,y.second),x.second);
}
是不是很简单??
再来一个:求平衡树中第k小的数
int find_id(int x,int k){
if(x==0)
return 0;
int ch0=Tree[x].ch[0];
if(Tree[ch0].sum>=k)
return find_id(ch0,k);
if(Tree[ch0].sum+1>=k)
return Tree[x].key;
return find_id(Tree[x].ch[1],k-Tree[ch0].sum-1);
}
额。。其实似乎这和普通的平衡树并没有不同。。。
求一个数val在平衡树中的排名(这个值可以不在树中)
int get_kth(int k){
//如果Split是按照前k个分离,还需要加一句int k1=find_id(root,k);并把下面的k-1改为k1
pair<int,int> x=Split(root,k-1);//这是Split按照小于等于k的分离的写法
int ans=Tree[x.first].sum+1;
root=Merge(x.first,x.second);
return ans;
}
是不是很简单???
总而言之,这是一个相当好写的平衡树,还不熟悉的话,可以多做几次cqoi的普通平衡树
其实,如果你学过其他的可持久化数据结构,
那么这部分就非常简单了(打个广告)
其实和主席树一样,我们只需要用n个root节点,并且每次插入时,将路径上每个节点复制一个即可。
这是可持久化的Split
pair<int,int> Split(int x,int k){
if(x==0)
return make_pair(0,0);
int newone;
pair<int,int> y;
if(Tree[x].key<=k){
newone=++ncnt;
Tree[newone]=Tree[x];
y=Split(Tree[newone].ch[1],k);
Tree[newone].ch[1]=y.first;
update(newone);
y.first=newone;
}
else{
newone=++ncnt;
Tree[newone]=Tree[x];
y=Split(Tree[newone].ch[0],k);
Tree[newone].ch[0]=y.second;
update(newone);
y.second=newone;
}
return y;
}
但其实有时候Merge操作并不需要复制新节点:
原因很简单,如果Merge的两个子树都在同一个时间版本内,
那复制新节点就毫无必要了。
int Merge(int x,int y){
if(x==0) return y;
if(y==0) return x;
if(Tree[x].fix<Tree[y].fix){
Tree[x].ch[1]=Merge(Tree[x].ch[1],y);
update(x);
return x;
}
else{
Tree[y].ch[0]=Merge(x,Tree[y].ch[0]);
update(y);
return y;
}
}
例题:洛谷3835普通可持久化平衡树
#include
#include
#include
#include
#define SF scanf
#define PF printf
#define MAXN 500010
using namespace std;
struct node{
int ch[2];
int key,sum,fix;
node () {}
node (int key1):key(key1),fix(rand()),sum(1) {}
}Tree[MAXN*30];
int ncnt,root[MAXN];
void update(int x){
Tree[x].sum=Tree[Tree[x].ch[0]].sum+Tree[Tree[x].ch[1]].sum+1;
}
int Merge(int x,int y){
if(x==0) return y;
if(y==0) return x;
if(Tree[x].fix<Tree[y].fix){
Tree[x].ch[1]=Merge(Tree[x].ch[1],y);
update(x);
return x;
}
else{
Tree[y].ch[0]=Merge(x,Tree[y].ch[0]);
update(y);
return y;
}
}
pair<int,int> Split(int x,int k){
if(x==0)
return make_pair(0,0);
int newone;
pair<int,int> y;
if(Tree[x].key<=k){
newone=++ncnt;
Tree[newone]=Tree[x];
y=Split(Tree[newone].ch[1],k);
Tree[newone].ch[1]=y.first;
update(newone);
y.first=newone;
}
else{
newone=++ncnt;
Tree[newone]=Tree[x];
y=Split(Tree[newone].ch[0],k);
Tree[newone].ch[0]=y.second;
update(newone);
y.second=newone;
}
return y;
}
int find_kth(int now,int val){
pair<int,int> x=Split(root[now],val-1);
int ans=Tree[x.first].sum+1;
root[now]=Merge(x.first,x.second);
return ans;
}
int get_kth(int x,int k){
if(x==0)
return 0;
int ch0=Tree[x].ch[0];
if(Tree[ch0].sum>=k)
return get_kth(ch0,k);
if(Tree[ch0].sum+1==k)
return Tree[x].key;
return get_kth(Tree[x].ch[1],k-Tree[ch0].sum-1);
}
int get_pre(int now,int val){
int k=find_kth(now,val);
return get_kth(root[now],k-1);
}
int get_bac(int now,int val){
pair<int,int> x=Split(root[now],val);
int ans=get_kth(x.second,1);
root[now]=Merge(x.first,x.second);
return ans;
}
void Insert(int val,int now){
pair<int,int> x=Split(root[now],val);
Tree[++ncnt]=node(val);
Tree[ncnt].ch[0]=0;
Tree[ncnt].ch[1]=0;
root[now]=Merge(Merge(x.first,ncnt),x.second);
}
void Delete(int val,int now){
pair<int,int> x=Split(root[now],val);
pair<int,int> y=Split(x.first,val-1);
if(Tree[y.second].key==val)
y.second=Merge(Tree[y.second].ch[0],Tree[y.second].ch[1]);
root[now]=Merge(Merge(y.first,y.second),x.second);
}
int n,las,x,tag;
int main(){
SF("%d",&n);
Insert(-2147483647,0);
Insert(2147483647,0);
for(int i=1;i<=n;i++){
SF("%d%d%d",&las,&tag,&x);
if(Tree[root[las]].sum!=0){
root[i]=++ncnt;
Tree[root[i]]=Tree[root[las]];
}
if(tag==1)
Insert(x,i);
if(tag==2)
Delete(x,i);
if(tag==3)
PF("%d\n",find_kth(i,x)-1);
if(tag==4)
PF("%d\n",get_kth(root[i],x+1));
if(tag==5)
PF("%d\n",get_pre(i,x));
if(tag==6)
PF("%d\n",get_bac(i,x));
}
}
指针版
#include
#include
#include
#include
#define SF scanf
#define PF printf
#define MAXN 500010
#define INF 2147483647
using namespace std;
struct node{
node *ch[2];
int val,sum,fix;
void pushup(){
sum=ch[0]->sum+ch[1]->sum+1;
}
}Tree[MAXN*24];
node *NIL=Tree,*ncnt=Tree;
typedef pair<node*,node*> PNN;
void Newnode(node *x,int val){
x->val=val;
x->sum=1;
x->fix=rand();
x->ch[0]=x->ch[1]=NIL;
}
void init(){
NIL->ch[0]=NIL->ch[1]=NIL;
}
node *Merge(node *x,node *y){
if(x==NIL)
return y;
if(y==NIL)
return x;
if(x->fix>y->fix){
x->ch[1]=Merge(x->ch[1],y);
x->pushup();
return x;
}
else{
y->ch[0]=Merge(x,y->ch[0]);
y->pushup();
return y;
}
}
PNN Split(node *x,int val){
if(x==NIL)
return make_pair(NIL,NIL);
node *newp=++ncnt;
PNN y;
if(x->val<=val){
*newp=*x;
y=Split(x->ch[1],val);
newp->ch[1]=y.first;
newp->pushup();
y.first=newp;
}
else{
*newp=*x;
y=Split(x->ch[0],val);
newp->ch[0]=y.second;
newp->pushup();
y.second=newp;
}
return y;
}
void Ins(node *&rt,int val){
PNN y=Split(rt,val);
Newnode(++ncnt,val);
node *x=ncnt;
node *z=Merge(x,y.second);
rt=Merge(y.first,z);
}
void Del(node *&rt,int val){
PNN x=Split(rt,val);
PNN y=Split(x.first,val-1);
if(y.second->val==val)
y.second=Merge(y.second->ch[0],y.second->ch[1]);
rt=Merge(Merge(y.first,y.second),x.second);
}
node *find_kth(node *x,int k){
if(x==NIL)
return x;
if(x->ch[0]->sum>=k)
return find_kth(x->ch[0],k);
if(x->ch[0]->sum+1>=k)
return x;
return find_kth(x->ch[1],k-(x->ch[0]->sum)-1);
}
node *find_nxt(node *x,int d){
while(x->ch[d]!=NIL)
x=x->ch[d];
return x;
}
node *root[MAXN];
int n;
int main(){
SF("%d",&n);
init();
root[0]=NIL;
Ins(root[0],-INF);
Ins(root[0],INF);
int las,tag,x;
for(int i=1;i<=n;i++){
SF("%d%d%d",&las,&tag,&x);
root[i]=++ncnt;
*root[i]=*root[las];
if(tag==1)
Ins(root[i],x);
if(tag==2)
Del(root[i],x);
if(tag==3){
PNN y=Split(root[i],x-1);
PF("%d\n",y.first->sum);
root[i]=Merge(y.first,y.second);
}
if(tag==4){
x++;
PF("%d\n",find_kth(root[i],x)->val);
}
if(tag==5){
PNN y=Split(root[i],x-1);
PF("%d\n",find_nxt(y.first,1)->val);
root[i]=Merge(y.first,y.second);
}
if(tag==6){
PNN y=Split(root[i],x);
PF("%d\n",find_nxt(y.second,0)->val);
root[i]=Merge(y.first,y.second);
}
}
}