diff --git a/TangDou/Topic/【换根】dfs专题.md b/TangDou/Topic/【换根】dfs专题.md index 8d4b954..6517225 100644 --- a/TangDou/Topic/【换根】dfs专题.md +++ b/TangDou/Topic/【换根】dfs专题.md @@ -858,20 +858,102 @@ Details: 5->4->1->2 10 - 1.第一次扫描,任选一个点为根,在 **有根树** 上执行一次树形$DP$。 - 2.第二次扫描,从刚才选出的根出发,对整棵树执行一次$DFS$,在每次递归前进行 **自上而下** 的推导,计算出 **换根** 之后的解。 +首先,我们选择结点$1$,然后树形$DP$一下: +1、求出$f$数组($f[i]$表示以$1$这根的树,以$i$为根的子树中流量的最大值)。 +2、设$g[u]$表示以$u$为源点,流向整个水系的最大流量,则显然$g[1]=f[1]$。假设$g[u]$已经求出,考虑其子结点$v$,则$f[v]$包含两部分: -首先,我们任选一个结点$root$,然后树形$DP$一下,求出$D_{root}$数组($D[i]$表示在以$i$为根的子树中流量的最大值)。然后设$f[x]$表示以$x$为源点,流向整个水系的最大流量,则显然 -$f[root]=D[root]$。假设$f[x]$已经求出,考虑其子结点$y$,则$f[y]$包含两部分: +- 1.从$v$流向以$v$为根的子树的流量,已经计算出来。 +- 2.从$v$沿着到父节点$u$的河道,进而流向水系中其他部分的流量。 -1.从$y$流向以$y$为根的子树的流量,已经计算出来。 -2.从$y$沿着到父节点$x$的河道,进而流向水系中其他部分的流量。 +由题意可得,从$u$流向$v$的流量为$min(f[v],c[u][v])$,所以从$u$流向除$v$以外其他部分的流量就是二者之差$g[u]−min(f[v],c[u][v])$。于是,把$v$作为源点,先流到$u$,再流向其他部分的流量就是把这个 **差** 再与$c[u][v]$取较小值后的结果。 -由题意可得,从$x$流向$y$的流量为$min(D[y],c[x][y])$,所以从$x$流向除$y$以外其他部分的流量就是二者之差$f[x]−min(D[y],c[x][y])$。于是,把$y$作为源点,先流到$x$,再流向其他部分的流量就是把这个“差”再与$c[x][y]$取较小值后的结果。 +$$ +\large \left\{\begin{matrix} +g[v]=f[v]+min(g[u]−min(f[v],c[u][v])−c[u][v]) & du[u]>1 \\ +g[v]=f[v]+c[u][v] & du[u]==1 +\end{matrix}\right. +$$ + +这是一个由上而下的递推方程,我们通过一次$dfs$即可实现。 -$if(du[x]>1)→f[y]=D[y]+min(f[x]−min(D[y],c[x][y])−c[x][y])$ -$if(du[x]==1)→f[y]=D[y]+c[x][y]$ +```cpp {.line-numbers} +#include +#include +#include +#include +using namespace std; +const int N = 2e5 + 10, M = N << 1; + +int n, du[N]; + +// 链式前向星 +int e[M], h[N], idx, w[M], ne[M]; +void add(int a, int b, int c = 0) { + e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; +} + +int f[N], g[N]; + +// 以1号点为根, 由子推父,先递归,再统计 +void dfs1(int u, int fa) { + for (int i = h[u]; ~i; i = ne[i]) { + int v = e[i]; + if (v == fa) continue; + dfs1(v, u); + if (du[v] == 1) // 如果v的度为1,也就是叶子 + f[u] += w[i]; + else + f[u] += min(f[v], w[i]); + } +} +void dfs2(int u, int fa) { + for (int i = h[u]; ~i; i = ne[i]) { + int v = e[i]; + if (v == fa) continue; + if (du[u] == 1) + g[v] = f[v] + w[i]; + else + g[v] = f[v] + min(g[u] - min(f[v], w[i]), w[i]); + // 先计算再递归 + dfs2(v, u); + } +} -这是一个由上而下的递推方程,我们通过一次DFS即可实现。 +int main() { + // 加快读入 + ios::sync_with_stdio(false), cin.tie(0); + int T; + cin >> T; + while (T--) { + // 初始化链式前向星 + memset(h, -1, sizeof h); + idx = 0; + memset(du, 0, sizeof du); + memset(f, 0, sizeof f); + memset(g, 0, sizeof g); + + cin >> n; + int a, b, c; + for (int i = 1; i < n; i++) { // 树,n-1条无向边 + cin >> a >> b >> c; + add(a, b, c), add(b, a, c); + du[a]++, du[b]++; // 记录入度,无向图就不谈入度和出度了 + } + // 第一遍dfs + dfs1(1, 0); + g[1] = f[1]; + + // 第二遍dfs + dfs2(1, 0); + + int ans = 0; + for (int i = 1; i <= n; i++) ans = max(ans, g[i]); + cout << ans << endl; + } + return 0; +} +``` ####[$CF708C$ $Centroids$](https://www.luogu.com.cn/problem/CF708C) https://blog.csdn.net/TheSunspot/article/details/118216638