POJ3585 Solution
题解
并不是网络流。
状态:\(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 (j\in e_i,v_j\not=fa_i)\\
f_{v_j}=dp_{v_j}+w_j\quad(j\in e_i,in_i=1)\\
f_{v_j}=dp_{v_j}+min(w_j,f_i-min(w_j,dp_{v_j}))\quad (j\in 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\n",ans);
}
return 0;
}