main
黄海 2 years ago
parent 267d3047f0
commit ffb8687436

@ -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

Loading…
Cancel
Save