左偏树

左偏树

左偏树是一种可并堆,学来做 slopetrick

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

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

根据这个定义,可以推出一棵以 x 为根的二叉树的大小至少为 2disx+11 ,同时也可以推出:对于一棵有 n 个节点的二叉树,其根节点的 dis 至多为 log(n+1)

左偏性质即为对于每个点 x ,都有 dislsdisrs

合并

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

对于两个小根堆的根 x,y ,假设 x<y ,不满足就交换。则 x 一定为当前子树的根。由于左偏,所以我们将 yx 的右子树向下递归合并。回溯的时候要维护左偏性质,即判断路径上的每一个点 x 是否都有 dislsdisrs ,不满足就交换。时间复杂度为 O(logn+logm) ,其中 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 @   Cyan_wind  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示