左偏树
左偏树以一种可以支持快速合并的数据结构。
我们对于左偏树中的每一个节点 \(u\),我们定义 \(d_u\) 表示以 \(u\) 为根的右链的长度。假设 \(u\) 的右儿子为 \(rs\),则 \(d_u = d_{rs} + 1\)。那么左偏树的一个性质就是对于所有节点 \(u\),假设其左右儿子分别为 \(ls,rs\),那么我们有 \(d_{ls} \ge d_{rs}\)。
根据上面的定义,我们可以知道左偏树没有深度保证,一条向左的链也满足左偏树的定义。
\(\text{merge}\)
考虑怎么合并两个左偏树,即设两个左偏树的根节点为 \(x,y\),我们希望能将这两棵左偏树合并为一颗新的左偏树。在此处我们假设 \(x \le y\),如果 \(x > y\) 的话交换一下 \(x,y\) 就行了。
那么这个 \(x\) 就是我们的新的左偏树的根节点,\(x\) 的左子树就是新的左偏树的左子树。接下来考虑将 \(y\) 和 \(x\) 的右节点递归合并,然后将合并后的节点作为 \(x\) 的右节点。为了保证还满足左偏的结构,如果此时有 \(d_{ls} < d_{rs}\),那么我们就交换 \(ls\) 和 \(rs\)。最后我们在更新 \(x\) 的右链的长度即可。
那么复杂度为 \(\mathrm O(\log n)\)。
\(\text{insert}\)
假如要插入一个节点,我们直接将其当成一棵只有一个节点的左偏树直接合并即可。
\(\text{delete}\)
先考虑删根怎么做。我们可以直接合并根的左右两个儿子。那么删除任意节点也是一样的,先将左右儿子合并,然后自底向上更新 \(d\)、不满足左偏性质时交换左右儿子,当 \(d\) 无需更新时结束递归。
\(\text{add}\)
直接在一个位置上打一个标记即可。
\(\text{Example 1}\)
对于每一个节点,我们维护一个左偏树,维护忍者的费用和和个数。假设我们现在合并完了节点 \(u\),那么我们可以贪心地将费用最高的忍者一次弹出,直到费用不超过预算。

浙公网安备 33010602011771号