作用
旋转Treap是非常经典的一种平衡树,但是由于旋转会破坏各种乱七八糟的东西,导致旋转Treap不能可持久化。而非旋转Treap根本不需要旋转,就可以维护堆性质和二叉排序树性质,从而实现可持久化(然而我太蒟蒻,依然不会可持久化,以后会了再更新)。当然,也可以和Splay一样进行区间操作(也不比Splay难写)。
实现
非旋转Treap和很多可并堆比如左偏树很像,一切操作基于Merge和Split,即合并与分裂(以下以大根堆为例)。
1.合并A和B
(合并的前提是A全小于B,否则只能启发式合并)
首先是递归边界:当A和B中有一个是null时,退出另一个。
如果A的fix(随机堆值)<B的fix,不能交换A和B,因为我们还要维护二叉排序树性质,所以就需要Merge(A,B->son[0])。这里还需要注意的是,不能Merge(B->son[0],A),因为要确保A全小于B。
如果A的fix>B的fix,同理,就Merge(A->son[1],B)。
2.将p分裂为前k个和剩下的部分
(简称左和右部分)
首先是递归边界:当p是null时,退出的两个部分为null和null。
如果k<=p->son[0]->si(子树节点个数),说明k在p的左儿子,那么就先把p->son[0]分裂为L(前k个)和R(剩下的部分),然后p->son[0]变成了R,整棵树分裂完成后左部分是L,而右部分是p。
如果k>p->son[0]->si,说明k在p的右儿子或p,由于向右走,所以要把p->son[1]分裂为L(前k-p->son[0]->si-1个)和R(剩下的部分),然后p->son[1]变成了L,整棵树分裂完成后左部分是p,右部分是R。
3.使用
分裂之后的合并就是A全小于B的合并,所以分裂与合并就可以很快速的改变Treap的结构,而不用旋转。
4.建树
非旋转Treap的建树不能像Splay一样分治建树,而要用笛卡尔树的建树方法来建树(因为笛卡尔树和Treap一模一样)。笛卡尔树的建树方法是维护最右边的一条路径,用栈储存这条路径上的节点(越深的节点越接近堆顶)。每当新来了一个节点now,就把now放到最右边,然而此时并不保证堆性质,于是把栈中所有fix小于now的fix的节点都从栈中删除,假设最后一个被删除的节点是lst,那么now的左儿子就是lst,当前栈顶(如果存在的话)的右儿子就是now。特别注意更新节点信息。
模板-普通
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define fst first
#define snd second
using namespace std;
const int maxn=100000,MAXINT=((1<<30)-1)*2+1;
int te;
//==================================================
struct Node
{
Node *son[2];
int si,val,fix; //val是二叉排序树权值
void Pushup() {si=son[0]->si+1+son[1]->si;}
};
typedef Node* P_node;
typedef pair<P_node,P_node> pnn;
Node tem[maxn+5];
P_node null=tem,len=null,ro=null;
P_node newNode(int k)
{
len++;len->son[0]=len->son[1]=null;
len->si=1;len->fix=rand();len->val=k;
return len;
}
P_node Merge(P_node A,P_node B) //合并
{
if (A==null) return B;if (B==null) return A;
if (A->fix<B->fix)
{
B->son[0]=Merge(A,B->son[0]);
B->Pushup();return B;
} else
{
A->son[1]=Merge(A->son[1],B);
A->Pushup();return A;
}
}
pnn Split(P_node p,int k) //分裂
{
if (p==null) return pnn(null,null);
pnn now;
if (k<=p->son[0]->si)
{
now=Split(p->son[0],k);
p->son[0]=now.snd;p->Pushup();
now.snd=p;
} else
{
now=Split(p->son[1],k-p->son[0]->si-1);
p->son[1]=now.fst;p->Pushup();
now.fst=p;
}
return now;
}
int getrank(P_node p,int k) //为避免重复,这个函数返回真正答案-1
{
if (p==null) return 0;
if (k<=p->val) return getrank(p->son[0],k); else
return p->son[0]->si+1+getrank(p->son[1],k);
}
void Insert(P_node &p,int x)
{
//插入等同于分裂出<=x的部分和>x的部分,然后插入新节点
pnn now=Split(p,getrank(p,x));
p=Merge(Merge(now.fst,newNode(x)),now.snd);
}
void Delete(P_node &p,int x)
{
//删除等同于分裂出<x的部分和>=x的部分,然后删除>=x部分的第一个
pnn now=Split(p,getrank(p,x));
p=Merge(now.fst,Split(now.snd,1).snd);
}
int getkth(P_node p,int k)
{
if (p==null) return MAXINT;
if (k==p->son[0]->si+1) return p->val; else
if (k<=p->son[0]->si) return getkth(p->son[0],k); else
return getkth(p->son[1],k-p->son[0]->si-1);
}
int getpre(P_node p,int k)
{
if (p==null) return MAXINT;
if (k>p->val)
{
int now=p->val,nxt=getpre(p->son[1],k);
if (nxt==MAXINT) return now; else return nxt;
} else return getpre(p->son[0],k);
}
int getsuf(P_node p,int k)
{
if (p==null) return MAXINT;
if (k<p->val)
{
int now=p->val,nxt=getsuf(p->son[0],k);
if (nxt==MAXINT) return now; else return nxt;
} else return getsuf(p->son[1],k);
}
//==================================================
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x)
{
int tot=0,f=1;char ch=getchar(),lst=' ';
while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}
if (lst=='-') f=-f;
while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
x=tot*f;
return Eoln(ch);
}
int main()
{
freopen("Treap.in","r",stdin);
freopen("Treap.out","w",stdout);
readi(te);
while (te--)
{
int td,x;readi(td);readi(x);
switch (td)
{
case 1:Insert(ro,x);break;
case 2:Delete(ro,x);break;
case 3:printf("%d\n",getrank(ro,x)+1);break;
case 4:printf("%d\n",getkth(ro,x));break;
case 5:printf("%d\n",getpre(ro,x));break;
case 6:printf("%d\n",getsuf(ro,x));break;
}
}
return 0;
}
模板-区间反转
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=100000,MAXINT=((1<<30)-1)*2+1;
int n,te;
//==================================================
struct Node
{
Node *son[2];
int si,val,fix;
bool tag_flip; //lazy-tag
void Pushup() {si=son[0]->si+1+son[1]->si;}
void Addflip() {swap(son[0],son[1]);tag_flip^=1;} //加标记
};
typedef Node* P_node;
struct pnn
{
P_node fst,snd;
pnn(P_node a=NULL,P_node b=NULL) {fst=a;snd=b;}
};
Node tem[maxn+5];
P_node null=tem,len=null,ro=null;
P_node newNode(int k)
{
len++;len->son[0]=len->son[1]=null;
len->si=1;len->fix=rand();len->val=k;len->tag_flip=false;
return len;
}
void Pushdown(P_node p) //传递标记
{
if (p->tag_flip)
{
p->tag_flip=false;
if (p->son[0]!=null) p->son[0]->Addflip();
if (p->son[1]!=null) p->son[1]->Addflip();
}
}
void LNR(P_node p)
{
if (p==null) return;Pushdown(p);
LNR(p->son[0]);printf("%d ",p->val);LNR(p->son[1]);
}
int top_b;P_node stk_b[maxn+5]; //代表最右边路径的栈
P_node Build(int L,int R)
{
top_b=0;stk_b[1]=null;
for (int i=L;i<=R;i++)
{
P_node now=newNode(i),lst=null;
while (top_b&&stk_b[top_b]->fix<now->fix)
{
stk_b[top_b]->Pushup(); //更新节点信息
lst=stk_b[top_b--]; //记录lst
}
if (top_b) stk_b[top_b]->son[1]=now;
now->son[0]=lst;stk_b[++top_b]=now;
}
while (top_b) stk_b[top_b--]->Pushup(); //更新节点信息
return stk_b[1]; //栈底就是根节点
}
P_node Merge(P_node A,P_node B)
{
if (A==null) return B;if (B==null) return A;
Pushdown(A);Pushdown(B);
if (A->fix<B->fix)
{
B->son[0]=Merge(A,B->son[0]);
B->Pushup();return B;
} else
{
A->son[1]=Merge(A->son[1],B);
A->Pushup();return A;
}
}
pnn Split(P_node p,int k)
{
if (p==null) return pnn(null,null);
pnn now;Pushdown(p);
if (k<=p->son[0]->si)
{
now=Split(p->son[0],k);
p->son[0]=now.snd;p->Pushup();
now.snd=p;
} else
{
now=Split(p->son[1],k-p->son[0]->si-1);
p->son[1]=now.fst;p->Pushup();
now.fst=p;
}
return now;
}
//==================================================
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x)
{
int tot=0,f=1;char ch=getchar(),lst=' ';
while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}
if (lst=='-') f=-f;
while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
x=tot*f;
return Eoln(ch);
}
int main()
{
freopen("Treap.in","r",stdin);
freopen("Treap.out","w",stdout);
readi(n);readi(te);ro=Build(1,n);
while (te--)
{
int x,y;readi(x);readi(y);y=y-x+1;
pnn l=Split(ro,x-1),r=Split(l.snd,y);r.fst->Addflip();
//分裂出1~x-1和x~n,再把后面分裂为x~y和y+1~n,加标记后合并
ro=Merge(l.fst,Merge(r.fst,r.snd));
}
LNR(ro);
return 0;
}