左偏树学习笔记

左偏树

左偏树是一种二叉的可并堆,故名思意,左偏树左边的节点多于右边节点

实现

对于堆中的每一个节点,维护一个 dd 值,定义为其往子树走最少多少步走到空节点

我们希望维护出来的堆一直往右走就能最快走到空

左偏树最核心的操作是“合并”

考虑合并两个大小分别为 n,mn,m 的堆 x,yx,y

取出一个更优的点,不妨是 xx,将 xx 的右儿子和 yy 递归合并

由于合并时一直在往右走,复杂度 O(logn+logm)O(logn+logm)

插入操作可视为堆与一个单独节点合并,删除堆顶可视为将堆顶左右儿子合并

值得一提的是,左偏树支持打懒标记(全体加/乘)

可持久化

左偏树支持可持久化,类似线段树合并,合并过程不断新建节点即可

若最初总共有 nn 个节点,考虑合并两个大小分别为 s1,s2s_1,s_2 的左偏树

其遍历节点数至多为 logs1+logs2logs_1+logs_2,最多合并 nn 次,时空复杂度 O(nlogn)O(nlogn)

应用

k短路

求s到t的第k短路

考虑先跑出反图上t点的最短路树 TT,复杂度 O(mlogm)O(mlogm)

考虑一条s到t的路径边集 PP,记去除 TT 上的边后的边集为 PP'

PP' 中相邻的两条边 e1,e2e_1,e_2 一定满足 e2e_2 的起点在 e1e_1 终点到 tt 的链上

显然,不同的 PP' 对应了不同的 PP,我们考虑维护 PP' 即可

对于已有的一个 PP',我们通过增加新边/替换最后一条边得到权值略大的集合 PP''

增加新边相当于我们要找到最小的一条以 PP' 中最后一条边的终点到 tt 的链上的一个点为起点的非树边

替换边相当于我们要找到次小的一条以 PP' 中倒数第二条边的终点到 tt 的链上的一个点为起点的非树边

我们考虑维护堆 H(x)H(x),储存点 xxtt 的链上的点为起点的非树边

显然我们不能暴力维护它,复杂度将达到 O(nmlogm)O(nmlogm)

发现 H(x)H(x)H(fax)H(fa_x) 有大量重合,我们维护一个可持久化堆即可,复杂度 O(mlogm)O(mlogm)

显然,上面提到的操作当我们维护出 H(x)H(x) 就已经迎刃而解了

总复杂度 O(nlogn+mlogm+klogk)O(nlogn+mlogm+klogk)

通过斐波那契堆优化最短路+维护堆套堆可以做到 O(nlogn+m+klogk)O(nlogn+m+klogk)

code