题解
并不是网络流。
状态:(dp_i)表示以(1)为根节点时,(i)子树到节点(i)的最大流量。(f_i)表示以(i)为根节点时的最大流量。
转移方程:设(in_i)表示节点(i)的度数,(fa_i)表示节点(i)的父节点,(e_i)表示与节点(i)相连的边集,(v_i)表示边(i)的终点,(w_i)表示边(i)的流量。
[dp_i=sum min(dp_{v_j},w_j)quad (jin e_i,v_j
ot=fa_i)\
f_{v_j}=dp_{v_j}+w_jquad(jin e_i,in_i=1)\
f_{v_j}=dp_{v_j}+min(w_j,f_i-min(w_j,dp_{v_j}))quad (jin e_i,in_i>1)
]
(dp)的转移方程同网络流。可以将水流反向,看作叶子节点为源点而根结点为汇点。当(in_i=1)时,则换根后(i)为叶子节点,贡献为(w_j)。否则贡献为(w_j)与除(v_j)子树外部分的最小值,而后者为(f_i)减去(v_j)子树对(i)结点的贡献,也就是(f_i-min(w_j,dp_{v_j}))。
目标状态:(f_i)
代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#define int long long
using namespace std;
const int N=4e5+10;
int fst[N],nxt[N],v[N],w[N],cnt;
int f[N],dp[N],in[N];
int read()
{
int s=0,ww=1; char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-') ww=-1; ch=getchar();}
while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar();
return s*ww;
}
void add(int x,int y,int z)
{
v[++cnt]=y,w[cnt]=z,in[y]++;
nxt[cnt]=fst[x],fst[x]=cnt;
}
void dfs1(int x,int fa)
{
for(int i=fst[x];i;i=nxt[i])
{
int y=v[i];
if(y==fa) continue;
dfs1(y,x);
if(in[y]==1) dp[x]+=w[i];
else dp[x]+=min(w[i],dp[y]);
}
}
void dfs2(int x,int fa)
{
for(int i=fst[x];i;i=nxt[i])
{
int y=v[i];
if(y==fa) continue;
if(in[x]==1) f[y]=dp[y]+w[i];
else f[y]=dp[y]+min(w[i],f[x]-min(dp[y],w[i]));
dfs2(y,x);
}
}
signed main()
{
int t=read(),n,ans,x,y,z;
while(t--)
{
n=read();
for(int i=0;i<=n;i++) fst[i]=dp[i]=f[i]=in[i]=0;
cnt=ans=0;
for(int i=1;i<n;i++)
{
x=read(),y=read(),z=read();
add(x,y,z),add(y,x,z);
}
dfs1(1,0);
f[1]=dp[1]; dfs2(1,0);
for(int i=1;i<=n;i++) ans=max(ans,f[i]);
printf("%lld
",ans);
}
return 0;
}