JZOJ 100003. 【NOI2017模拟.4.1】 Tree(费用流)

JZOJ 100003. 【NOI2017模拟.4.1】 Tree

题目

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

1
5 3
1 2 1
1 4 1
2 3 1
2 5 1
1 3 2
1 5 4
1 4 3

Sample Output

7

Data Constraint

在这里插入图片描述

题解

  • 这题单纯从树的方面考虑的确没什么想法,因为正解是要连边跑最小费用最大流。
  • 做法其实很简单:
  • 首先设源点 S S S和汇点 T T T,令每条路径上深度大的为起点,深度小的为终点
  • 每条树边上从深度大的点向深度小的连边,容量就是边的最大覆盖次数,费用为 0 0 0
  • 每条路径上从起点向终点连边,容量为 1 1 1,费用为路径的收益,
  • 源点向每条路径的起点连边,每条路径的终点向汇点连边,容量为 1 1 1,费用为 0 0 0
  • 不难发现,这样子的最大流一定是路径的总数 m m m,那么最小费用代表什么呢?
  • 可以这样理解,首先认为每条路径都是要选的,收益总和为 s u m sum sum
  • 如果直接从路径的起点不经树边到达终点,相当于这条路径不选,需要花费收益(相当于减去了收益),不占用树边的容量,
  • 如果经过树边到达终点,相当于这条路径选,不需要花费收益,占用了树边的容量。
  • s u m sum sum减去费用 s s s,剩下的就是所有选出的边的总收益,
  • 因为 s s s是最小费用,所以 s u m − s sum-s sums是最大收益。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10010
#define ll long long
int last[N],nxt[3*N],to[3*N],len;
int dp[N],S,T,n,m;
int p[N],q[N],bz[N];
ll dis[N],ans;
struct
{
   int x,y,w;
}a[N];
struct
{
   int to,w;
   ll c;
}e[N*3];
void add(int x,int y)
{
   to[++len]=y;
   nxt[len]=last[x];
   last[x]=len;
}
void add1(int x,int y,int w,ll c)
{
   e[++len].to=y;
   e[len].w=w;
   e[len].c=c;
   nxt[len]=last[x];
   last[x]=len;
}
void dfs(int x,int fa)
{
   for(int i=last[x];i;i=nxt[i]) if(to[i]!=fa)
   {
   	dp[to[i]]=dp[x]+1;
   	dfs(to[i],x);
   }
}
int id=0;
int SPFA()
{
   dis[S]=0,p[S]=1,q[1]=S;
   bz[S]=++id;
   int l=0,r=1;
   while(l!=r)
   {
   	l=l%(n+5)+1;
   	int x=q[l];
   	for(int i=last[x];i;i=nxt[i]) if(e[i].w)
   	{
   		int y=e[i].to;
   		if(dis[x]+e[i].c<dis[y]||bz[y]!=id)
   		{
   			bz[y]=id;
   			dis[y]=dis[x]+e[i].c;
   			if(!p[y])
   			{
   				r=r%(n+5)+1;
   				q[r]=y;
   				p[y]=1;
   			}
   		}
   	}
   	p[x]=0;
   }
   return dis[T]<1e+16&&bz[T]==id;
}
int count(int k,int flow)
{
   if(k==T) return flow;
   int have=0;
   p[k]=1;
   for(int i=last[k];i;i=nxt[i]) if(e[i].w)
   {
   	int x=e[i].to;
   	if(!p[x]&&dis[k]+e[i].c==dis[x])
   	{
   		int t=min(flow-have,e[i].w);
   		int now=count(x,t);
   		ans-=e[i].c*now;
   		have+=now,e[i].w-=now,e[i^1].w+=now;
   		if(have==flow) break;
   	}
   }
   p[k]=0;
   return have;
}
int main()
{
   int tn;
   scanf("%d",&tn);
   while(tn--)
   {
   	int i,x,y;ll c;
   	scanf("%d%d",&n,&m);	
   	memset(last,0,sizeof(last));
   	len=0;
   	for(i=1;i<n;i++) 
   	{
   		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
   		add(a[i].x,a[i].y),add(a[i].y,a[i].x);
   	}
   	dp[1]=1;
   	dfs(1,0);
   	memset(last,0,sizeof(last));
   	len=1;
   	for(i=1;i<n;i++)
   	{
   		if(dp[a[i].x]>dp[a[i].y]) swap(a[i].x,a[i].y);
   		add1(a[i].y,a[i].x,a[i].w,0);
   		add1(a[i].x,a[i].y,0,0);
   	}
   	S=n+1,T=n+2,ans=0;
   	for(i=1;i<=m;i++)
   	{
   		scanf("%d%d%lld",&x,&y,&c);
   		ans+=c; 
   		if(dp[x]>dp[y]) swap(x,y);
   		add1(S,y,1,0),add1(y,S,0,0);
   		add1(y,x,1,c),add1(x,y,0,-c);
   		add1(x,T,1,0),add1(T,x,0,0);
   	}
   	while(SPFA()) while(count(S,1e+6));
   	printf("%lld
",ans);
   }
   return 0;
} 
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/13910047.html