- 左偏树是可并堆的一种实现,它是一棵具有堆和左偏的性质的二叉树
- 几个定义
- 外顶点:左儿子或右儿子为空的顶点
- 距离:该节点到子树中最近外顶点的距离(为了方便描述,默认空节点的距离为-1)
- 每个节点记录的信息
- 权值:满足父节点的权值 ≥ 子节点权值(堆)
- 距离:满足左节点的距离 ≥ 右节点的距离(左偏)
一些操作
合并两个可并堆
- 由于左偏的性质我们希望将小的左偏树插在右儿子上使得相对平衡些
- 若一棵为空,另一棵树直接插在空节点的位置即可
- 由于一棵左偏树中任意子树也是左偏树,假设当前要将树B合并到A中,根据堆的性质,若A的根顶的权值大于B,就交换A,B
- 然后合并B和A的右子树,合并后若dis[lson[A]]<dis[rson[A]]就交换lson[A]和rson[A]
- 每次插入后要更新当前根顶点的距离值
- 合并的复杂度为 O(logsizeA+logsizeB)
- 合并是左偏树的基础,就像Splay里的旋转一样
- 返回值为以
a
为堆顶和以
b 为堆顶的堆合并后的堆顶
function merge(a,b:longint):longint; begin if (a=0) then exit(b); if (b=0) then exit(a); if (val[a]>val[b]) then swap(a,b); rson[a]:=merge(rson[a],b); if (dis[lson[a]]<dis[rson[a]]) then swap(a,b); dis[a]:=dis[rson[a]]+1; exit(a); end;
构建左偏树
- 逐个顶点合并进去的复杂度为 O(NLogN)
- 我们用一个队列,初始是每个点,每次从队列头取两个根顶合并为一个,将合并后的根顶扔到队列尾,直至队列中只剩一个
- 复杂度为 O(N)
- 插入/删除根顶
- 插入就是和一个点合并
- 删除根顶就是合并根顶的两个子树
删除已知位置的节点
- 左偏树不能像平衡树一样按照权值找到要删除的点
- 设要删出的位置为
a
,
p=fa[a] - 那么我们删掉
a
后,现将
a 的两个子堆合并, q=merge(lson[a],rson[a]) - 然后将
q
的和
p 连起来 - 然后一路向上修改 dis 值
复杂度不超过 O(logN)
procedure delete(a:longint); var p,q:longint; begin p:=fa[a]; q:=merge(lson[a],rson[a]); if (p<>0)and(lson[p]=a) then lson[p]:=q; if (p<>0)and(rson[p]=a) then rson[p]:=q; while (p<>0) do begin if (dis[lson[p]]<dis[rson[p]]) then swap(lson[p],rson[p]); if (dis[p]=dis[rson[p]]+1) then exit; dis[p]:=dis[rson[p]]+1; p:=fa[p]; end; end;
-
- 题目大意
支持
- 1.合并两个团
- 2.询问某个点所在的团的最小值,并删除
- 题解
- 合并两个堆
- 删除堆顶
- CODE
- 题目大意
左偏树
最新推荐文章于 2022-12-19 14:48:00 发布