|
|
|
@ -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 <iostream>
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
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
|
|
|
|
|