考完学波数据结构
左偏树
左偏树是可并堆的一种,可以在 $O(\log n)$ 的时间内实现堆的合并,它是通过额外维护一个距离并保证性质来实现的(有点类似于点分治找重心)
定义和性质
- 距离:一个点的距离定义为一个点到离它最近的空节点的距离
- 性质:每个点的左儿子的距离一定大于等于其右儿子的距离
以上就是左偏树额外维护以保证时间复杂度的信息,不难发现,这棵二叉树(堆)的每个节点的距离一定于右儿子的距离加1
定理:根节点的距离 $dis_{root} \le \log (n + 1)$ ,证明如下:
设 $f(k)$ 表示 $dis_{root} = k$ 的子树至少包含多少点,易得, $f(1) = 1 \le 2^1 - 1$ ,若 $f(k - 1) \le 2^{k - 1} - 1$ ,则对于 $dis_{root} = k$ 的子树,右儿子的距离为 $k - 1$ ,即右子树最少有 $f(k - 1)$ 个点,而左儿子的距离一定大于等于其右儿子的距离,故左子树最少也有 $f(k - 1)$ 个点,则 $f(k) = f(k - 1) + f(k - 1) + 1$ ,即 $f(k) \le 2^k - 1$ ,由数学归纳法原理, $f(k) \le 2^k - 1$ ,则 $f(\log (n + 1)) \le 2^{\log (n + 1)} - 1 = n$ ,可得 $dis_{root} \le \log (n + 1)$
合并
有了以上定义和性质,我们来看看如何合并左偏树,假设我们合并的两个左偏树的根分别为 $a, b$ ,进行如下操作(假设是小根堆):
- 若 $b < a$ , $swap(a, b)$
- 将 $a$ 作为合并后的根节点,并将 $a$ 的左子树作为合并后的左子树
- 递归合并 $a$ 的右子树和 $b$ ,将合并后的树作为右子树
- 若右子树的距离大于左子树,交换两个子树
可以发现,每一次递归,两个根节点必有一个会变成自己的右子树,即它的距离会减1,当根的距离为1时它就没有右子树了,故时间复杂度为 $O(dis_a - 1 + dis_b - 1) = O(\log (n + 1)) = O(\log n)$
代码:
1 | struct LeftHeap |
例题
左偏树的难点主要在于应用
约定
先做一个变化:令 $a’_i = a_i - i, b’_i = b_i - i$ ,这样,最小值不会改变,且任意一组解都有唯一的 $\{b’i\}$ 与之对应,而变化的目的是 $b_i < b{i + 1},即b’i + i < b’{i + 1} + i + 1$ ,故 $b’i \le b’{i + 1}$ ,我们把 $<$ 变成了 $\le$ ,为了方便,以下不会再用到原数组,所有的 $\{b_i\}$ 都指的是 $\{b’_i\}$
转化
考虑一个子问题:若有 $n$ 个数 $\{a_i\}$ ,求一个数 $b$ ,使得 $\sum_{i = 1}^n \mid a_i - x \mid$ 最小,即在保证 $b_1 = b_2 = … = b_n = x$ 的情况下求解原问题,明显,取 $\{a_i\}$ 的中位数时最小
那么再考虑,如果有 $n$ 个数 $\{a_i\}$ ,其中对于 $\sum_{i = 1}^m \mid a_i - b_i \mid$ ,其取最小值时 $b_{1 \sim m} = u$ ,对于 $\sum_{i = m + 1}^n \mid a_i - b_i \mid$ ,其取最小值时 $b_{m + 1 \sim n} = v$ ,那么该如何合并 $a_{1 \sim m}$ 和 $a_{m + 1 \sim n}$ 呢?分两类讨论:
- 若 $u \le v$ ,即满足 $\{b_i\}$ 的单调性,则两段分别保留自己的 $b$
- 若 $u > v$ ,即不满足单调性,则使 $b_{1 \sim n}$ 为 $a_{1 \sim n}$ 的中位数
正确性
第一类正确性很好理解,下面证明第二类做法的正确性(即取中位数后 $\sum_{i = 1}^n \mid a_i - b_i \mid$ 最小):
设对于 $a_{1 \sim n}$ 的一组最优解为 $\{b_i\} = \{x_i\}$
若 $x_m > u$ ,则由单调性可得 $x_{m + 1 \sim n} > u$ ,由于 $b_{1 \sim m} = u$ 是对于 $a_{1 \sim m}$ 的最优解,所以用 $u$ 替换 $x_{1 \sim m}$ 仍然满足单调性且不会使答案变差,故只需讨论 $x_m \le u$ 的情况,同理,也只需讨论 $x_{m + 1} \ge v$ 的情况,因此只需考虑 $x_m \le u$ 且 $x_{m + 1} \ge v$ 的情况
引理
下面证明一个引理:
若对于 $a_{1 \sim n}$ 的最优解为 $\{b_i\} = k$ ,则对于任意一组解(不一定最优) $\{b_i\} = \{x_i\}$ 满足 $x_1 > k$ (因为单调性,其实就是 $\forall i \in [1, n], x_i > k$ ),将其改为 $\{b_i\} = t(k \le t \le x_1)$ 一定不会比 $\{b_i\} = \{x_i\}$ 差
如图,将解 $\{b_i\} = \{x_i\}$ 替换成 $\{b_i\} = t$ 答案不会变差
先证明:将解变为 $\{b_i\} = x_1$ 答案不会变差
考虑将第二段及以后的 $x$ 整体下移 $\Delta$ 个单位使得第二段平移到于第一段水平的位置,如图:
考虑被平移部分的 $a$ 的中位数,设其为 $k’$ ,若 $k’ > k$ 那么对于被平移部分,取 $k’$ 显然比取 $k$ 更优,则与 $k$ 的“最优”性质矛盾,所以必有 $k’ \le k$ (此时因为单调性不能让 $k$ 等于 $k’$ ),因此被平移部分至少有一半的 $a$ 是小于等于 $k$ 的,我们将对应的 $x$ 下移,会使这些所有数的答案分别变优 $\Delta$ ,而另外 $a$ 大于 $k$ 的部分答案会变差 $\Delta$ 但变优的比变差的多,所以总的答案不会变差;以此类推,将每一段都下移,解不会变差
然后我们来看原命题,就变得很简单了,由 $k$ 的最优性, $\{b_i\} = k$ 一定比 $\{b_i\} = x_1$ 优,而 $x_1$ 越靠近 $k$ 就越优(准确的说是不会变差),所以取在 $x_1, k$ 间的任意值 $t$ 替代 $x_1$ 答案不会变差
需要注意的是,当 $k$ 比 $\{x_i\}$ 都大时,结论任成立,即上图的情况反过来也行
正确性2
回到刚从证明正确性
刚才已经说过,只需考虑 $x_m \le u$ 且 $x_{m + 1} \ge v$ 的情况,即如图:
$u, v$ 以 $m$ 为界,分别时前一半的最优解和后一半的最优解,红色部分是一组整体的最优解,由刚才的引理,红色部分可以替换为绿色部分,解不会变差,也就是说:存在一组全段都相等的最优解,明显,此时应该是整段的中位数
于是转化的做法的正确性成立
实现
下面考虑如何用左偏树实现该过程
类似动态维护中位数的思想,我们用左偏树维护较小一半数,其最大值就是中位数
设当前段中位数为 $u$ ,新段中位数为 $v$ ,合并完后中位数为 $w$
每次需要合并的情况一定是新段的中位数小于当前中位数,即 $v < u$ 时,则易证合并完毕后新的中位数一定在原来两个中位数之间,即 $v \le w \le u$ ,此时如果新的中位数是新段中的某个数,由于新段的左偏树只维护了小于 $v$ 的数,所以我们就找不到 $w$ 了,换句话说,在没有限制的情况下,直接用左偏树维护中位数是不行的
但是,这道题还有一个特殊性质:每次新加的一段只有一个数(当然这个数就是中位数),换句话说,如果 $w$ 是在新加入的一段中,那么 $w$ 只可能等于 $v$ ,应为新段没有别的数了
但是又有一个问题:如果在某次插入完后,中位数变小了,导致不满足单调性(换句话说,变得比前一段小了),我们必须合并这个刚完成插入的段和前一段,此时这个段不是只有一个数啊
同上设原中位数为 $u$ ,新中位数为 $w$ ,而这一段的前一段中位数为 $k$ ,显然有 $w < k < u$
我们考虑到,这一段插入了一个新的点,中位数只会变化一个位置,换句话说,在这一段上 $w$ 和 $u$ 之间是没有数的( $k$ 在前一段上),而 $k < u$ ,故在这一段上, $w, k$ 之间也是没有数的,所以这一段和前一段合并后的中位数要么是 $w$ ,要么在前一段上,不会出现在这一段上且大于 $w$ 的情况
于是可以用左偏树维护较小一半数来合并(注意维护较大的一半是不行的,因为合并后中位数会变小)
代码
1 |
|