左偏树

左偏树

左偏树是一种可并堆,学来做 \(slope-trick\)

左偏树满足堆的性质,以及一个新的左偏性质。

先放定义:定义外部节点为没有左儿子或右儿子的节点。定义一个节点的距离(dis)为它到子树内最近的外部节点的距离,特别的,空节点的 \(dis\) 为 -1 。这是为了打代码方便,使得外部节点的 \(dis\) 可以初始化为 0。

根据这个定义,可以推出一棵以 \(x\) 为根的二叉树的大小至少为 \(2^{dis_x+1}-1\) ,同时也可以推出:对于一棵有 \(n\) 个节点的二叉树,其根节点的 \(dis\) 至多为 \(\left\lceil\log(n+1)\right\rceil\)

左偏性质即为对于每个点 \(x\) ,都有 \(dis_{ls}\ge dis_{rs}\)

合并

可并堆最重要的就是合并了!

对于两个小根堆的根 \(x,y\) ,假设 \(x<y\) ,不满足就交换。则 \(x\) 一定为当前子树的根。由于左偏,所以我们将 \(y\)\(x\) 的右子树向下递归合并。回溯的时候要维护左偏性质,即判断路径上的每一个点 \(x\) 是否都有 \(dis_{ls}\ge dis_{rs}\) ,不满足就交换。时间复杂度为 \(O(\log n+\log m)\) ,其中 \(n,m\) 为合并的两个堆的大小。

其他操作

加入:直接当作合并来做

删除:若删除的点为根,则直接合并左右子树就好。若删除的点不为根,则需要额外维护每个点的父节点,并且在合并完儿子后,需要向上维护左偏性质。

Code

#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5;
struct Date{
	int ls,rs,dis,co,fa;long long x;
};Date d[N*2];
struct QUE{
	int op,x,y;long long z;
};QUE que[N];
int ss,rt[N],tot,a[N];long long ans[N];
int heap_newnode(long long k)
{
	tot++;d[tot].x=k;d[tot].co=tot;rt[tot]=tot;d[tot].dis=0;return tot;
}
int heap_find(int x)
{
	if(d[x].co==x)
	return x;
	return d[x].co=heap_find(d[x].co);
}
int heap_merge(int x,int y)
{
	if(x==0||y==0)
	return x+y;
	if(d[x].x>d[y].x)
	swap(x,y);
	d[x].rs=heap_merge(d[x].rs,y);
	d[d[x].rs].fa=x;
	if(d[d[x].ls].dis<d[d[x].rs].dis)
	swap(d[x].ls,d[x].rs);
	d[x].dis=d[d[x].rs].dis+1;
	return x;
}
void heap_update(int x)
{
	if(x==0)
	return;
	if(d[d[x].ls].dis<d[d[x].rs].dis)
	swap(d[x].ls,d[x].rs);
	heap_update(d[x].fa);
}
void heap_del(int x,int y)
{
	x=heap_find(x);
	if(rt[x]==y)
	rt[x]=heap_merge(d[y].ls,d[y].rs);
	else
	{
		x=heap_merge(d[y].ls,d[y].rs);
		if(d[d[y].fa].ls==y)
		d[d[y].fa].ls=x;
		else
		d[d[y].fa].rs=x;
		d[x].fa=d[y].fa;
		heap_update(x);
	}
}
long long heap_min(int x)
{
	x=heap_find(x);
	return d[rt[x]].x;
}
void heap_Merge(int x,int y)
{
	x=heap_find(x);y=heap_find(y);
	d[y].co=x;
	rt[x]=heap_merge(rt[x],rt[y]);
}
int main()
{
	int n,m,i,x,y;
	scanf("%d%d",&n,&m);
	d[0].dis=-1;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&x);
		a[i]=heap_newnode(x);
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d",&que[i].op);
		if(que[i].op==0)
		scanf("%d%d",&que[i].x,&que[i].y);
		if(que[i].op==1)
		scanf("%d",&que[i].x);
		if(que[i].op==2)
		scanf("%d%d",&que[i].x,&que[i].y);
		if(que[i].op==3)
		scanf("%d%d%lld",&que[i].x,&que[i].y,&que[i].z);
	}
	for(i=1;i<=m;i++)
	{
		x=a[que[i].x];y=a[que[i].y];
		if(que[i].op==0)
		heap_del(x,y);
		if(que[i].op==1)
		ans[++ss]=heap_min(x);
		if(que[i].op==2)
		heap_Merge(x,y);
		if(que[i].op==3)
		{
			heap_del(x,y);
			heap_Merge(x,a[que[i].y]=heap_newnode(que[i].z));
		}
	}
	for(i=1;i<=ss;i++)
	printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2024-11-29 19:58  Cyan_wind  阅读(27)  评论(0)    收藏  举报