虚树教程

问题引入

SDOI2011消耗战

虚树

按照朴素的做法,就是对于每一个询问都跑一边DP。

\[Dp[i] = Dp[ i ] + \min( Dp[Son],Cost[i,Son]) \,\,\,(Son不为关键点)\\ Dp[i] = Dp[ i ] + Cost[ i, Son ] \,\,\,(Son为关键点)\\ Dp[i] = INF\,\,\,(i为叶子) \]

这样时间上显然是不行的。我们需要优化一下。

注意到\(\sum k\leqslant 500000\),那么可能许多点都不是关键点,而我们却浪费了许多时间在它们上面。其实删掉一个关键点,只需要断掉路径上边权最小的边即可。

这么说来,我们可以缩掉很多不是关键点的点。但是为了保持原树的形状,我们同时需要维护每两个点之间的最近公共祖先(LCA)。其实到这里就可以尝试上手了。下面提供一个可能比较优美的虚树构建方法:

首先按照所有关键点的DFS序排序。开一个 Stack ,加入根节点。

顺序遍历排好序的所有关键点,求关键点和栈顶的Lca。记栈顶元素为Top,栈顶第二个元素为 PreTop(如果有的话)。分为以下几种情况考虑:

  • Deep[ Lca ] = Deep[ Top ]:那么直接把当前点加入 Stack 即可。
  • Deep[ Lca ] < Deep[ Top ] : 如果Deep[ PreTop ] > Deep[ Lca ],则在新树种加入边PreTop-Top,并且弹出栈顶元素,直到 Deep[ PreTop ] <= Deep[ Lca ]。 然后再分两种情况 : 1)如果Deep[ PreTop ] = Deep[ Lca ],在新树种加入PreTop-Top,弹出栈顶,然后加入当前点;2)如果Deep[ PreTop ] < Deep[ Lca ], 在新树种加入Top-Lca,弹出栈顶,然后加入Lca和当前点。
  • 最后顺序弹出栈顶,并同时加入边。

那么构造到此结束了。这样如果预处理了Lca,就可以线性构造虚树了。

开头所提的问题可以参考这里

posted @ 2019-09-26 20:54  chy_2003  阅读(155)  评论(0编辑  收藏  举报