题目链接 — South Central China 2008 Accumulation Degree
Description
(给你一棵有n个结点的树,每一条边连接u_i和v_i)
(流量为w_i,你需要找出一个结点最为root,并且求出从root出发到其他所有叶子结点的流量最大值)
(数据范围1leq nleq 200000,并且sum n leq200000)
Solution
(如果你一来就想到网络流,说明你网络流没学好,树形dp相关的也没学好(笑))
(我们要求出所有root中的最大值,毫无疑问肯定是要对每个结点都求一次的)
(关键在于思考,如果对每一个点作为root去dfs跑他的子树,肯定超时。那么有没有什么方法状态转移)
(在处理转移之前我们先维护一个root的最大流量,我们默认root为结点1,然后对他跑个dfs维护出flow[i])
(flow[i]表示当前结点i所有子树流到流向他的值)
(转移公式 sum_{所有子结点i} flow[p]+= min(w,flow[i]) 注:w为子结点到父节点的流量值,min是限流)
(在处理好以1为根结点的流量数组flow[]之后,我们来考虑向他的子结点转移)
(观察上面这副图,假设当前结点为u,子结点为v,设某点的流量为f[i].)
(则当前结点的最大流量 f[u] = x + min(y,z);)
(其子结点的最大流量为f[v] = z+min(x,y);)
(我们已知flow[u] = sum_{所有子结点i}flow[i])
(那么f[u]-min(flow[v],w)就表示u子结点v以外子结点的流量贡献,也就是上图的x)
(而子结点flow[v]即表示为上图的z,同时已知y即为w_{u->v})
(所以可以得到f[v]=z+min(x,y)=flow[v]+min(f[u]-min(flow[v],w),w))
(这样转移方程也就出来了)
Code
#include<bits/stdc++.h>
typedef long long ll;
#define IOS ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
const int maxn = 200010;
const int inf = 0x3f3f3f3f;
//思路:从每一个叶子结点跑,然后维护出到当前点最小的值,作为该节点对叶子结点的贡献值
struct node{
int v; int w;
};
int deg[maxn];
int sum[maxn];
int sum1[maxn];
vector<node>E[maxn];
void dfs(int p,int fa){
for(int i=0;i<(int)E[p].size();i++){
int v = E[p][i].v; int w = E[p][i].w;
if(v!=fa){
dfs(v,p);
if(deg[v] == 1){
sum[p] += w;
}else{
sum[p] += min(w,sum[v]);
}
}
}
return ;
}
void dfs1(int p,int fa){
for(int i=0;i<(int)E[p].size();i++){
int v = E[p][i].v; int w = E[p][i].w;
if(v!=fa){
if(deg[p] == 1){
sum1[v] = w;
}else{
sum1[v] = sum[v] + min((sum1[p]- min(sum[v],w)),w);
}
dfs1(v,p);
}
}
}
int main(){
IOS
int T;
cin>>T;
while(T--){
int n;
cin>>n;
for(int i=1;i<=n;i++) {
E[i].clear(),deg[i]=0;
sum1[i] = sum[i] = 0;
}
for(int i=1;i<=n-1;i++){
int u,v; int w;
cin>>u>>v>>w;
E[u].push_back((node){v,w});
E[v].push_back((node){u,w});
deg[u]++; deg[v]++;
}
dfs(1,0);
sum1[1] = sum[1];
dfs1(1,0);
int ans = -inf;
for(int i=1;i<=n;i++){
ans = max(ans,sum1[i]);
}
cout<<ans<<endl;
}
}