Treap,它不像splay那样功能强大,在速度上也远不如AVL、RBT等平衡树,但是FHQ Treap的诞生却改变了这一切。FHQ Treap的核心操作是分裂与合并,这种操作方式使得它天生支持维护序列、可持久化等特性。它几乎可以实现splay的所有功能,尽管在速度上没有什么优势,但相对于其它平衡树来说代码较短且容易实现。
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
const int INF=0x7fffffff;
const int N=2000010;
struct Node
{
int l,r;
int key,size;
unsigned int val;
}tr[N];
mt19937 mt_rand(233);
int root,tot,rt1,rt2,rt3;
int New(int key)
{
tr[++tot].key=key;
tr[tot].val=mt_rand();
tr[tot].size=1;
return tot;
}
void pushup(int p)
{
tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+1;
}
void split(int now,int k,int &x,int &y)
{
if(!now)x=y=0;
else{
if(tr[now].key<=k)//按关键值分裂,并且分裂时BST性质和大根堆性质不会受到影响
x=now,split(tr[now].r,k,tr[now].r,y);
else y=now,split(tr[now].l,k,x,tr[now].l);
pushup(now);
}
}
int merge(int x,int y)//x树的key均小于等于y树是合并的基础,这可以保证合并时满足BST性质
{
if(!x||!y)return x+y;
if(tr[x].val>tr[y].val)//保证大根堆性质
{
tr[x].r=merge(tr[x].r,y);//将y挂在x身上,注意参数顺序不要搞反,不然就凉了
pushup(x);
return x;
}
else
{
tr[y].l=merge(x,tr[y].l);//将x挂在y身上
pushup(y);
return y;
}
}
int kth(int rt,int rank)//求以rt为根的子树中的第K大数,它也是求前驱和后继的基础
{
int now=rt;
while(now)
{
if(tr[tr[now].l].size+1==rank)break;
if(tr[tr[now].l].size>=rank)now=tr[now].l;
else
{
rank-=tr[tr[now].l].size+1;
now=tr[now].r;
}
}
return now?tr[now].key:INF;//找不到就返回INF
}
void insert(int x)
{
split(root,x,rt1,rt2);
rt1=merge(rt1,New(x));
root=merge(rt1,rt2);
}
void erase(int x)
{
split(root,x,rt1,rt3);
split(rt1,x-1,rt1,rt2);
rt2=merge(tr[rt2].l,tr[rt2].r);
root=merge(merge(rt1,rt2),rt3);
}
int getrank(int x)
{
split(root,x-1,rt1,rt2);
int ans=tr[rt1].size+1;
root=merge(rt1,rt2);
return ans;
}
int pre(int x)
{
split(root,x-1,rt1,rt2);
int ans=kth(rt1,tr[rt1].size);
root=merge(rt1,rt2);
return ans;
}
int nxt(int x)
{
split(root,x,rt1,rt2)