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;
}
posted @ 2021-05-28 17:21  violet_holmes  阅读(39)  评论(0编辑  收藏  举报