[模板] 左偏树

左偏树

用途

支持\(O(\log n)\)合并,\(O(\log n)\)删除堆顶,\(O(\log n)\)查找,\(O(1)\)取最小值的堆.

定义

对于左偏树中的一个节点x,离它最近的一个叶子结点经过的边数称为它的距离,记为 \(dist(x)\) .特别地,外结点的距离为0,空节点(null)的距离为-1.

对于左偏树中的任意节点,满足它的左子树的距离大于等于右子树的距离.

代码

int val[nsz];
struct tnd{int v,l,r,d,f;}tree[nsz];
void init(){rep(i,1,n)tree[i].v=val[i];}
int merge(int a,int b){//b -> a
	if(a==0||b==0)return a+b;
	if(tree[a].v>tree[b].v||(tree[a].v==tree[b].v&&a>b))swap(a,b);
	tree[a].r=merge(tree[a].r,b);
	tree[tree[a].r].f=a;
	if(tree[tree[a].l].d<tree[tree[a].r].d)swap(tree[a].l,tree[a].r);
	tree[a].d=tree[tree[a].r].d+1;
	return a;
}
int getf(int x){
	while(tree[x].f)x=tree[x].f;
	return x;
}
void del(int x){
	val[x]=-1;
	tree[tree[x].l].f=tree[tree[x].r].f=0;
	merge(tree[x].l,tree[x].r);
}

//merge two heaps
	if(val[a]==-1||val[b]==-1||a==b)continue;
	a=getf(a),b=getf(b);
	if(a==b)continue;
	merge(getf(a),getf(b));

//delete top
	if(val[a]==-1)continue;
	else a=getf(a),cout<<val[a]<<'\n',del(a);

板子,bzoj1455-罗马游戏

随机偏堆

每次合并的时候, 不需要判断两边的深度或者大小, 直接 rand() 得到要合并的儿子.

容易发现期望意义下复杂度是正确的, 与左偏树相同.

炒鸡好写...

int merge(int u,int v){
    int p=val[u]>val[v]?u:v,x=rand()&1;
    return (u&&v)?(ch[p][x]=merge(ch[p][x],u+v-p),p):(u|v);
}
posted @ 2018-10-29 16:17  Ubospica  阅读(130)  评论(0编辑  收藏  举报