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 sum−s是最大收益。
代码
#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\n",ans);
}
return 0;
}
哈哈哈哈哈哈哈哈哈哈