简介
堆:可以查找集合最大值,支持插入元素,以及删除最大元素。
可并堆就在此基础上,支持快速合并两个集合的操作。
左偏树
首先,定义外节点为:其右儿子为空的节点。
定义距离为每个点到其子树中最近外接点的距离。
现在保证左儿子的距离值必须 不小于 右儿子的距离值。
这样,就能保证每个点往右走,走到叶子节点的步数不超过 l o g ( n ) log(n) log(n)步,其中 n n n表示这个节点的子树大小。
支持的操作:
1、合并
非常类似范浩强treap的合并方式。
比较简单,直接上代码
node *merge(node *x,node *y){
if(x==NIL) return y;
if(y==NIL) return x;
if(x->val > y->val)
swap(x,y);
x->ch[1]=merge(x->ch[1],y);
if(x->ch[1]->dis > x->ch[0]->dis)
swap(x->ch[0],x->ch[1]);
x->dis=x->ch[1]->dis+1;
return x;
}
2、删除根节点(或任意指定位置的节点)
void del(node *&x){
x=merge(x->ch[0],x->ch[1]);
//若是删除非根节点,则还需要再把x的子树和根合并
}
其他操作实在太简单,这里就不再赘述。
板题:洛谷P3377
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
struct node *NIL;
struct node{
node *ch[2],*fa;
int val,dis;
}Tree[MAXN];
void Newnode(node *x,int val){
x->val=val;
x->ch[0]=x->ch[1]=x->fa=NIL;
}
node *rt[MAXN];
int n,m;
void init(){
NIL=Tree;
NIL->ch[0]=NIL->ch[1]=NIL->fa=NIL;
for(int i=1;i<=n;i++)
rt[i]=Tree+i;
}
int fa[MAXN];
int get_fa(int x){
if(fa[x]==0)
return x;
fa[x]=get_fa(fa[x]);
return fa[x];
}
node *merge(node *x,node *y){
if(x==NIL)
return y;
if(y==NIL)
return x;
if(x->val > y->val)
swap(x,y);
x->ch[1]=merge(x->ch[1],y);
if(x->ch[1]->dis > x->ch[0]->dis)
swap(x->ch[0],x->ch[1]);
x->dis=x->ch[1]->dis+1;
return x;
}
bool del[MAXN];
int main(){
SF("%d%d",&n,&m);
init();
int val,tag,x,y;
for(int i=1;i<=n;i++){
SF("%d",&val);
Newnode(Tree+i,val);
}
for(int i=1;i<=m;i++){
SF("%d",&tag);
if(tag==1){
SF("%d%d",&x,&y);
if(del[x]||del[y])
continue;
int fax=get_fa(x),fay=get_fa(y);
if(fax==fay)
continue;
node *fx=rt[fax];
node *fy=rt[fay];
fa[fay]=fax;
rt[fax]=merge(fx,fy);
}
else{
SF("%d",&x);
if(del[x]){
PF("-1\n");
continue;
}
int fax=get_fa(x);
node *fx=rt[fax];
del[fx-Tree]=1;
PF("%d\n",fx->val);
rt[fax]=merge(fx->ch[0],fx->ch[1]);
}
}
}