题目描述
有一个树形的水系,由 N-1 条河道和 N 个交叉点组成。
我们可以把交叉点看作树中的节点,编号为 1~N,河道则看作树中的无向边。
每条河道都有一个容量,连接 x 与 y 的河道的容量记为 c(x,y)。
河道中单位时间流过的水量不能超过河道的容量。
有一个节点是整个水系的发源地,可以源源不断地流出水,我们称之为源点。
除了源点之外,树中所有度数为 1 的节点都是入海口,可以吸收无限多的水,我们称之为汇点。
也就是说,水系中的水从源点出发,沿着每条河道,最终流向各个汇点。
在整个水系稳定时,每条河道中的水都以单位时间固定的水量流向固定的方向。
除源点和汇点之外,其余各点不贮存水,也就是流入该点的河道水量之和等于从该点流出的河道水量之和。
整个水系的流量就定义为源点单位时间发出的水量。
在流量不超过河道容量的前提下,求哪个点作为源点时,整个水系的流量最大,输出这个最大值。
解法
加入这是一棵有根树,我们可以直接利用树形DP来进行求解。
设 表示以 为根的子树中,以 为源点时的最大流量。
若当前根节点为 ,子树的根节点为 。
- 当 时,有且仅有一条河道,可以直接累加权值。
- 当 时,我们需要将 和边权 进行比较,显然两者取最小值即可。
状态转移方程就是:
每一个点按照上述方法做一遍树形DP,时间复杂度:
解法
由于这是一棵无根树,不同的根会产生不同的答案,故我们可以思考一下如何进行换根。
换根的主要思路就是如何处理根与根的转化。
设 表示以 为根的子树中,答案的最大值。
显然,对于 到 的转移,可以这么考虑:
- 不变的答案为:在以 为根的树中,以 为根的子树的答案。即
- 变化的答案为: 上面的答案要变成以j为根的答案。即以 为根的总答案减去在原子树中,以 为根的总答案,在不考虑路径限制的情况下,即为j上面的答案;再与 取最小值即可。我们现在考虑如何求出前者:以 为根的总答案为 ,以j为根的答案为 .现在转移方程就一目了然了。
- 对于单独的点,直接以边权为答案即可。
有状态转移方程:
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, ans, tot;
int in[N], f[N], g[N], Link[N];
struct edge { int y,v,next; } e[N*2];
inline int read(void)
{
int s = 0, w = 1; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
return s * w;
}
void clear(void)
{
ans = 0, tot = 0;
memset(in,0,sizeof in);
memset(Link,0,sizeof Link);
return;
}
void add(int x,int y,int v)
{
tot ++;
e[tot] = edge{y,v,Link[x]};
Link[x] = tot;
return;
}
void dfs(int x,int fa)
{
g[x] = 0;
for (int i=Link[x];i;i=e[i].next)
{
int y = e[i].y;
int v = e[i].v;
if (y == fa) continue;
dfs(y,x);
if (in[y] == 1) g[x] += v;
else g[x] += min(g[y],v);
//判断流量是否会超出当前容量
}
return;
}
void dp(int x,int fa)
{
for (int i=Link[x];i;i=e[i].next)
{
int y = e[i].y;
int v = e[i].v;
if (y == fa) continue;
if (in[x] > 1) f[y] = g[y]+min(f[x]-min(g[y],v),v);
if (in[x] == 1) f[y] = g[y]+v;
dp(y,x);
}
return;
}
void work(void)
{
clear();
n = read();
for (int i=1;i<n;++i)
{
int x = read(), y = read(), v = read();
add(x,y,v), add(y,x,v);
in[x] ++, in[y] ++;
}
dfs(1,0), f[1] = g[1], dp(1,0);
for (int i=1;i<=n;++i) ans = max(ans,f[i]);
printf("%d\n", ans);
return;
}
int main(void)
{
freopen("degree.in","r",stdin);
freopen("degree.out","w",stdout);
int T = read();
while (T --) work();
return 0;
}