今天打 \(Atcoder\) 时遇到了一道换根 \(DP\) ,发现自己不太会,学习了一下。
一般来说,这类题在做树形 \(DP\) 时没有固定的根,可以枚举根然后做 \(N\) 次树形 \(DP\) ,但复杂度是不太优的,于是有了换根 \(DP\),一般通过两次对整棵树的 \(dfs\) 遍历来求解,复杂度一般是线性的。
还是用例题来直观讲解吧。
[POJ3585] Accumulation Degree
题意
有一个 \(n\) 个结点的树形水系,每条 \(u->v\) 边有一个容量 \(C(u,v)\) ,任意一个点都可以作为起点,树中每个度数为 \(1\) 的结点都可以作为终点,求最大流量。
(其实就是求树的最大流)
\(n\le10^5\)
Solution
(直接上网络流还是算了吧)
先思考朴素的 \(DP\) 解法,枚举树根 \(S\) ,对每个 \(S∈ [1,n]\) 做一次树形 \(DP\) ,设 \(dp[i]\) 表示当前流向以 \(i\) 为根的子树的最大流量,那么转移方程很好写。
时间复杂度 \(O(N^2)\),过不了本题数据。
发现此题满足换根的特征,考虑换根 \(DP\) 。
设 \(f[i]\) 表示以 \(i\) 为根流向整颗树的最大流量,如果我们能线性计算出 \(f\) 数组,那么我们的答案只需要取 \(f\) 中的最大值。
任选一个点为根,做一次普通的树形 \(DP\) 。然后考虑这样一件事,对于一条边 \(u->v\) ,现在 \(f[u]\) 的值已经被求出,如何计算出\(f[v]\) 呢?
显然,已经知道了一个重要的信息,从 \(u\) 结点流向整颗树的最大流量,而从 \(u\) 到 \(v\) 的流量(也是到 \(v\) 子树内所有叶子结点的最大流量和)是 \(min(d[v],C(u,v))\) ,那我们除去这一部分,得到的就是 \(u\) 到除了 \(v\) 所在子树的流量和,现在我们进行一个神奇的操作,令 \(v\) 为整棵树的根。
那么我们就会发现这样的事情, \(u\) 成了 \(v\) 的一个儿子,且子树和是 \(f[u]-min(d[v],C(u,v))\) ,这条边流量仍然是 \(C(u,v)\) 。
我们有换根的方程:
\(f[v]\) 的值就表示树根从 \(u\) 换成 \(v\) 的结果,时间复杂度 \(O(N)\) 。
我们发现,这种问题的一般特征是,通过一次整体的,固定根的树形 \(DP\) 对整体进行一个求解,这个过程是自下而上的,再通过自上而下的换根操作,关注将一条边的端点进行换根的影响,因为已知上面的点在全局的值,除去子树贡献,可以方便转移。
参考文献
李煜东.《算法竞赛进阶指南》. 二次扫描与换根法
[HDU2196] Computer
题意
给你一棵 \(n\) 个点的树,输出以每个顶点为根节点,到树上点的边权和最大,输出这个最大值。
Solution
初看这道题,可能会像上一道题一样,考虑换根时除去子树贡献,但这题要求取 \(max\) ,而不是求和,不好拆分,这类题,就需要在记录最大值同时,记录一个次大值,然后就可以换根转移了,和求树的直径是一样的,这题所有答案取最大值就是这棵树的直径,复杂度 \(O(N)\) 。
设 \(f[i][0]\) 表示点 \(i\) 到以它为根的子树的最远点的距离
\(f[i][1]\) 表示点 \(i\) 到以它为根的子树(并且这个子树与最远点所在子树不相同)的次远点的距离(一会会用到)
\(f[i][2]\) 表示除去点 \(i\) 的子树后,点 \(i\) 到离它最远的点的距离
\(val\) 表示边权
那么 \(f[v][2]\) 怎么求?(\(u\) 表示 \(v\) 的父亲)
\(if(f[v][0] + val[i] == f[u][0])\) \(f[v][2] = f[u][1] + val[i];\)
\(else\) \(f[v][2] = f[u][0] + val[i];\)
\(f[v][2] = max(f[v][2], f[u][2] + val[i]);\)
那么一个点 \(i\) 到它的最远点的距离即为 \(max(f[i][0],f[i][2])\)