洛谷 P6199 [EER1]河童重工 题解

题面

题意:

给定两棵 \(n\) 个点的带边权树。

求包含 \(n\) 个点的,\(i\)\(j\) 之间边权为 \(d_1\left(i, j\right) + d_2\left(i, j\right)\) 的完全图的最小生成树大小。

其中 \(d_x\left(i, j\right)\) 表示 \(i\)\(j\) 两点在第 \(x\) 棵树上的距离。

数据范围:\(n\le10^5\)

子问题:Tree MST

先来考虑子问题的解法:

  • 有一个结论:如果我们把边集分成两部分,对这两部分分别求 MST,然后再合并起来,就是原图的 MST。
  • 由于问题建立在树上,因此考虑树分治。
  • 在点分治的过程中,考虑跨过分治重心的路径。设分治重心为点 \(x\)\(f_u=dis_{u,x}+w_u\),那么把两个点 \(u\)\(v\)连起来的边权就是 \(f_u+f_v\)。很明显,对于当前分治重心控制的所有节点,肯定是都和 \(f\) 值最小的点连起来最优,将这些边加入边集中。
  • 对于在同一个子树内的两个点 \(u\)\(v\),这样连边的边权大于实际边权,但没有关系,这样肯定不优,而实际边权会在之后的递归中被考虑到,所以可以照样加进去。
  • 最后对于选出来的边跑一遍 Kruskal 即可。
  • 时间复杂度 \(\mathcal{O}(n\log^2n)\)

考虑将子问题的解法扩展到当前问题。

对第二棵树点分治,那么对于当前分治重心 \(x\) 来说,两个点 \(u\)\(v\) 的边权就是 \(d_2\left(u,x\right)+d_2\left(v,x\right)+d_1\left(u,v\right)\)

\(x\) 这一层的所有节点在第一棵树上的虚树拿出来,并且对每一个点 \(u\) 建一个虚点 \(u+n\),将它们俩连起来,边权为 \(d_2\left(u,x\right)\)

对于虚树上的每一个点 \(u\),找到离它最近的虚点 \(fr_u\)。对于每一条边 \(\left(u,v\right)\),如果离它们两个点最近的虚点不同,那么在最终边集中加入 \(\left(fr_u,fr_v,w_{u,v}+dis_u+dis_v\right)\),其中 \(dis_u\) 表示 \(u\) 和与它最近的虚点的距离。对于最终边集跑一边 Kruskal 即可。

这样做为什么是对的?考虑加入到最终边集中的边边权一定不会小于实际边权(因为加进的边一定合法),对于最终最小生成树上的一条边 \(\left(u,v,w\right)\),当分治重心在 \(u\)\(v\) 的路径上时,这条边就会加入。

时间复杂度 \(\mathcal{O}\left(n\log^2n\right)\)

Code:https://pastebin.ubuntu.com/p/fN3Rbg5mHV/

posted @ 2022-03-17 21:03  csxsi  阅读(20)  评论(0编辑  收藏  举报