Kruskal重构树

Kruskal重构树

这个东西看起来非常的猛...听起来逼格也很高.

但其实并不难,它是基于单调性和 \(Kruskal\) 的最小生成树算法构造出的.

很多人初学重构树,都以为重构树必然是 \(MST\) 相关的.

但更多时候,我们是利用了重构树的结构特点和迷人的单调性.

重构树的基本思想或许是从离线算法延伸而出的.

这里我们就以一个经典的重构树模型从离线算法谈起.

给定一张有 \(n\) 个点, \(m\) 条边的无向图.边有边权,点有点权.
给出 \(q\) 组询问,每次询问从一个点出发,只经过边权小于等于一个数能到达的点中点权最大的一个.

\(1\le n \le 10^5,m \le 4\times 10^5,1 \le q \le 10^5\)

这里很多同学都会秒出一个离线算法:

把边按照边权排序,询问按照询问的边权排序,遍历询问,每次用并查集维护连通,同时对并查集加权,即最大值.
这样复杂度是正确的,由于排序后的单调性,不需要每次重新维护并查集,只需要在上一次的基础上继续加边至不存在一条边小于等于当前询问的边权且不在并查集中即可.

复杂度瓶颈在于排序,总复杂度大概是 \(\Theta(m \times log_2{m} + q\times log_2{n})\)

足以通过本题.

那么如果强制在线怎么做呢?

这时候也会有同学秒出一个在线做法:

可持久化并查集.这显然是完全可以的.

把边排序之后加一遍边同时维护历史版本,每次询问相当于询问一个历史版本中的并查集,直接维护即可.

但......这好难写啊,半天调不出来怎么办?

有没有更简便的,不需要大数据结构的做法呢?

答案是可以的.

这就是我们今天要说的 \(Kruskal\) 重构树.

同样是按边权把边排序.

每次合并的时候,新建一个节点,该节点的点权为当前边的边权.(注意使用路径压缩,重构树的树高是没有保证的.)

这样,很显然,重构树的节点从深到浅点权不降.

所以我们的一次查询就变成了,在重构树上从这个点向上,深度最浅的点权不大于给定值的点.

答案就是该点子树内的叶子节点的点权最大值,这个合并的时候维护即可.

那么怎么去找到这个深度最浅的点权不大于给定值的点呢?

前面提到过,重构树的点权具有深度相关的单调性,所以我们可以直接倍增去找.

于是,这样就做完了上面那题,复杂度瓶颈在于排序和倍增.

总复杂度 \(\Theta(m\times log_2{m}+q\times log_2{n})\)

这时候,睿智的你一定已经发现了,上面那题其实就是 \(NOI2018\: D1\: T1\) 归程的魔改版.

恭喜你,你现在已经能切掉 \(NOI\)\(D1 \: T1\) 了!

例题:

重构树其实是个套路,一般重构树的题目只要理解了重构树,都能秒出重构树做法,所以例题不再讲解.

[NOI2018]归程

Peaks

CodeForce1213G

三道重构树的题目应该已经足够理解.

posted @ 2019-10-16 11:14  Phecda  阅读(185)  评论(0编辑  收藏  举报

Contact with me