luogu 1084

原题链接

题意

给定1棵点数为\(n\),带边权的树。树边为双向边,根节点为\(1\)

给出\(m\)支军队及其所在的点;可能存在多支军队在同1点上。

你可以沿着树边移动军队。但任意一支军队最终不能停留在1号点上。

最终你要保证所有的叶子结点到根的路径上都至少有1个点上有军队。

最小化各支军队移动的路径长度的最大值。如果无法完成,输出\(-1\)

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

题解

考虑\(-1\)的情况:\(m\)小于根结点的直接儿子数。

考虑二分答案,二分军队移动长度的最大值,判断能否覆盖所有的叶子结点。

考虑贪心。为了方便表述,下文称根节点的儿子为儿子,称以儿子为根的子树为直接子树。

为了最大程度地覆盖叶子结点,每个军队在移动距离不超过\(mid\)的前提下不断向上跳父亲,不过即使\(mid\)足够大,还是只能停留在儿子上。这之后,我们将某些能够跨越根节点造福社会的自由点保存起来。具体地,对于每个儿子,至多给自己保留剩余距离最小的1支军队(甚至不用保留),其余的放入1个\(set\)。然后依次扫描各个儿子,若该直接子树中仍有叶子结点未被覆盖,则在\(set\)中查找能够到达该儿子且剩余距离最小的点,并删除它;若某次查找失败,则\(return false\)

在距离不超过\(mid\)的前提下跳父亲,可以通过树上倍增实现。军队对叶子结点的覆盖,可以用\(DFS\)序加差分实现。结合二分,时间复杂度\(O(nlognlogsum)\)

值得一提的是,自己做的时候,我认为如果儿子需要军队且它本身有军队时,应将自己的军队中剩余最小的留给自己,其余的移到根上用于帮助别人。这是1个错误的贪心,我竟拿到了80分。回顾本题中贪心的依据,最大化地利用剩余距离大的点,而可能存在某种情况,某点上的最小军队的剩余距离很大,但该点与根节点的距离很小,这时用它造福某个离根节点距离很远的点,它自己由另外1个到达根节点后剩余距离很小的点造福,答案可能更优。

我们重新考虑军队的分配。可以明确几点:

  1. 如果某军队可以到达某儿子但无法到达1号点,那么一定令其驻扎在该儿子。
  2. 我们只需考虑能够到达1号点的军队分配到未被驻扎的儿子中的最优情况。依旧只需要讨论某个未驻扎儿子上剩余距离最小的那支军队是否需要保留在原位置(设该儿子为\(x\),该军队为\(cur\))。假设\(cur\)不保留在原位置,那么一定用于造福某个比\(x\)离根节点更远的点。原因是如果\(cur\)用于造福某个比\(x\)离根近的点\(y\),意味着有另1个剩余距离能到\(x\)的军队\(help\);这时如果调整为将\(cur\)留给\(x\)、将\(help\)留给\(y\),一定能够实现且结果不变。

[代码见此](https://github.com/littlewyy/OI/blob/master/luogu 1084.cpp)

posted @ 2019-10-30 07:41  littlewyy  阅读(130)  评论(0编辑  收藏  举报