里面有一堆调试信息,可以不用管。
里面的注释是辅助本蒟蒻理解的,也不用管。
前面头文件部分因为写得太长删掉了,导入的是万能头。
代码较长,码风也不是很好,望各位大佬仔细帮我挑毛病,感谢!
/*
FHQ-Treap大体思想:
FHQ-Treap是FHQ发明的一种Treap,
这种Treap只依靠分裂与合并维护自身的平衡,
分裂时依照某个值的大小分为两棵FHQ-Treap(值域分裂),
合并时利用键值来维护树的高度。
*/
const int N=1e5+100;
struct TreapNode
{
int l,r;// 左右儿子
int siz,cnt;// 字树大小 副本数
int val,key;// 存储值 键值
void Set(int v)// 初始化一个新节点
{
l=r=0;
siz=cnt=1;
val=v;
key=rand();// 键值随机
}
}t[N<<1];
int root;
#define lc(x) t[x].l
#define rc(x) t[x].r
int stk[N],idx,top;// 用于新建&回收节点
int New(int v)// 新建立一个节点,返回节点编号以插入
{
int u=(top?stk[top--]:++idx);// 优先用回收站内的
t[u].Set(v);
return u;
}
void pushup(int u)// 更新一个节点的size内容
{
t[u].siz=t[lc(u)].siz+t[rc(u)].siz+t[u].cnt;
}
void Split(int p,int v,int &x,int &y)// 按v的大小分裂为左右棵FHQ-Treap,树根分别为x和y
{
if(!p) return ;
if(t[p].val<=v)
{
// 如果我分裂后属于左树
// 那么我的左子树和我一定是左树
// 接下来访问我的右子树,因为右子树中也有一部分属于左树
x=p;
Split(rc(p),v,rc(p),y);
}
else
{
// 反向同理
y=p;
Split(lc(p),v,x,lc(p));
}
pushup(p);
}
int Merge(int x,int y)// 合并两棵FHQ-Treap,返回树根
{
if(!x||!y) return x+y;// x或y为空,返回不为空的
if(t[x].key<t[y].key)
{
// 如果x的键值小于y的键值
// 那么根据堆性质,x应放在y之上
// 又由于x树上所有值均小于y树上所有值
// 所以合并x的右子树和y树
// 最后x即为两树之根,返回x
rc(x)=Merge(rc(x),y);
pushup(x); return x;
}
else
{
// 反之同理
lc(y)=Merge(x,lc(y));
pushup(y); return y;
}
}
void Insert(int v)
{
if(!root)// 如果没有根的话
{
// 新节点就是根
root=New(v);
return ;
}
int x=0,y=0,z=0,w=0;
Split(root,v-1,x,z);
Split(z,v,y,w);
// 把树劈成3份(x,y,w),x是小于v的,y是等于v的,w是大于v的
if(!y)// 如果没有插入过v,y应为空
y=New(v);// 新建节点,值为v
else
++t[y].cnt;// 否则直接增加副本数
root=Merge(x,Merge(y,w));// 再把三份树都拼回来
}
void Delete(int v)
{
int x=0,y=0,z=0,w=0;
Split(root,v-1,x,z);
Split(z,v,y,w);
// 把树劈成3份(x,y,w),x是小于v的,y是等于v的,w是大于v的
if(t[y].cnt>1)// 如果v有副本,直接删副本
--t[y].cnt;
else
stk[++top]=y,y=0;// 否则回收编号y,y归为0(为Merge操作做准备)
root=Merge(x,Merge(y,w));// 拼回来,如果y没有了,就是直接拼x和w
}
int GetPre(int v)
{
int x=0,y=0,z=0;
Split(root,v-1,x,y);// x存储小于v的,y存储大于等于v的
z=x;// 留一下x原来的位置
while(rc(x)) x=rc(x);// 小于v中最大的就是v的前驱
swap(z,x);// x恢复到原来的位置,z指向v的前驱
root=Merge(x,y);// 合并分裂过的两棵树
return t[z].val;
}
int GetNxt(int v)// 同上
{
int x=0,y=0,z=0;
Split(root,v,x,y);
z=y;
while(lc(y)) y=lc(y);
swap(y,z);
root=Merge(x,y);
return t[z].val;
}
int GetVal(int p,int rnk)
{
if(rnk>t[lc(p)].siz+t[p].cnt)// 如果排名已经大于我左子树的大小加上我的副本数
return GetVal(rc(p),rnk-t[lc(p)].siz-t[p].cnt);// 要找的会在右子树
// 记得扣掉排名中属于我的和我左子树的
if(rnk>=t[lc(p)].siz)// 再剩下的情况中,如果大于我左子树的大小
return t[p].val;// 要找的就是我,返回
return GetVal(lc(p),rnk);// 否则就在左子树里
}
int GetRank(int v)
{
int x=0,y=0,z=0;
Split(root,v-1,x,y);// x存储小于v的,y存储大于等于v的
z=t[x].siz+1;// 排名就是比我小的个数+1
root=Merge(x,y);// 合并两棵树
return z;// 返回存储好的结果
}
void DEBUG_TreeOut(int p)// Debug
{
if(!p) return ;
DEBUG_TreeOut(lc(p));
printf("\nNow:%d\nLeft Son:%d\nRight Son:%d\nVal:%d Key:%d\n",p,lc(p),rc(p),t[p].val,t[p].key);
DEBUG_TreeOut(rc(p));
}
int n,opt,x;
signed main()
{
cin>>n;
for(int i=1;i<=n;++i)
{
// printf("\n\nINPUT:");
scanf("%d%d",&opt,&x);
switch(opt)
{
case 1:
Insert(x);
break;
case 2:
Delete(x);
break;
case 3:
printf("%d\n",GetRank(x));
break;
case 4:
printf("%d\n",GetVal(root,x-1));
break;
case 5:
printf("%d\n",GetPre(x));
break;
case 6:
printf("%d\n",GetNxt(x));
break;
}
//DEBUG_TreeOut(root);
}
return 0;
}