【暖*墟】#树形DP# 虚树的学习与练习

虚树,就是不真实的树。往往出现在一类树形动态规划问题中。

换句话说,虚树就是为了解决一类树形动态规划问题而诞生的。

【例题1】[SDOI2011]消耗战  

 

  • 给出一棵树,每条边有边权。有m次询问,每次询问给出k个点。
  • 问使得这k个点均不与1号点(根节点)相连的最小代价。
  • 数据范围:n<=250000, m>=1, ∑k<=500000。

[暴力dp] 首先考虑m=1,也就是只有一次询问的情况。

我们考虑暴力dp:设f[x]为处理完以x为根的树的最小代价。

转移分为两种情况:1.断开自己与根的联系,代价为从根到该节点的最小值;

2.若该节点不是询问点、不考虑断开,求断开子树内的所有询问点的最小代价;

但是这样的复杂度是O(nm)的,显然无法AC。

然而我们发现∑k是比较小的,可不可以对k下手呢?于是,虚树诞生了。

虚树的主要思想:对于一棵树,仅仅保留有用的点,重新构建一棵树。

  • 这里有用的点指的是询问点和它们的lca。

比如这样的一棵树:

对于样例中的三次询问:

3
2 10 6 4 5 7 8 3 3 9 4 6

那么它的虚树分别长这样:

          

  • 注意:图二中,断了5就一定会断了7、8,所以不用考虑7、8。

得到了询问点,考虑如何构造出一棵虚树。

首先要先对整棵树dfs一遍,求出dfs序,每个节点以dfs序为关键字从小到大排序。

同时 维护一个栈,表示 从根到栈顶元素这条链

 

假设当前要加入的节点为p,栈顶元素为x=s[top],lca为他们的最近公共祖先。

 

因为我们是按照dfs序遍历,因此lca不可能是p。

 

那么现在会有两种情况: 1. lca是x,直接将p入栈。

2. x,p分别位于lca的两棵子树中,那么此时x这棵子树已经遍历完毕。

 

上述2中,如果没有遍历完毕,则x的子树中还有一个未加入的点y,

但是dfn[y]<dfn[p],即:应先访问y。所以没有遍历完毕。

 

当x这棵子树已经遍历完毕时,我们需要对其进行构建。

 

设栈顶元素为x,栈顶第二个元素为y(到达x的某一条链)。

若dfn[y]>dfn[lca],可以连边y—>x,将x出栈;

若dfn[y]=dfn[lca],即y=lca,连边lca−>x,子树构建完毕;

若dfn[y]<dfn[lca],即lca在y,x之间,连边lca−>x,x出栈,lca入栈,子树构建完毕。

不断重复这个过程,虚树就构建完成了。另外需要维护链上最小值,然后直接在虚树上dp。


 

  • 借某大佬的图解:

 代码实现:

 

猜你喜欢

转载自www.cnblogs.com/FloraLOVERyuuji/p/10498493.html