【jzoj100003】【Tree】【网络流】

11 篇文章 0 订阅

题目大意

这里写图片描述

解题思路

先按树边从上到下建图容量为能走的次数费用为零,能选的链从下往上连容量为一费用为价值的边。跑最大费用循环流即可,这个东西我也是第一次听说。
对于本题,我们强制费用为正的边先流,这样我们就破坏了流量平衡,新建源和汇,入度减出度为正的点从源流容量为差值费用为零的边,为负的点流汇容量为差值费用为零的边,跑最小费用最大流,用强制流的费用和减去最小费用即为答案。

code

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define Min(a,b) ((a<b)?a:b)
#define Max(a,b) ((a>b)?a:b)
#define Fo(i,j,k) for(int i=j;i<=k;i++)
#define Fd(i,j,k) for(int i=j;i>=k;i--)
#define For(i,j) for(int i=Begin[j];i;i=Next[i])
using namespace std;
int const Mxn=1e5+9,Mxm=1e6+9,Inf=1e9;LL In=1e18;
int T,N,M,Gra,Begin[Mxn],Vis[Mxn],Du[Mxn],To[Mxm],Size[Mxm],
    Len[Mxm],Next[Mxm];
LL Ans,Dis[Mxn];
void Insert(int U,int V,int X,int Y){
    To[++Gra]=V;
    Size[Gra]=X;
    Len[Gra]=Y;
    Next[Gra]=Begin[U];
    Begin[U]=Gra;
}
int Add(int Now,int T,int Mi,LL Co){
    Vis[Now]=1;
    if(Now==T){
        Ans-=1ll*Mi*Co;
        return Mi;
    }
    For(i,Now)if(Size[i]&&(!Vis[To[i]])&&(Dis[Now]==Dis[To[i]]+Len[i])){
        int Tmp=Add(To[i],T,Min(Mi,Size[i]),Co+Len[i]);
        if(Tmp>0){
            Size[i]-=Tmp;
            Size[i^1]+=Tmp;
            return Tmp;
        }
    }
    return 0;
}
int Updis(){
    LL Tmp=In;
    Fo(i,0,N+1)if(Vis[i])For(j,i)if((!Vis[To[j]])&&Size[j])
        Tmp=Min(Tmp,Dis[To[j]]+Len[j]-Dis[i]);
    if(Tmp==In)
        return 0;
    Fo(i,0,N+1)if(Vis[i])Dis[i]+=Tmp;
    return 1;
}
int main(){
    //freopen("tree.in","r",stdin);
    //freopen("tree.out","w",stdout);
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d",&T);
    Fo(cas,1,T){
        scanf("%d%d",&N,&M);
        int A,B,D;
        Fo(i,0,N+1)Begin[i]=Du[i]=Dis[i]=0;Gra=1;
        Fo(i,1,N-1){
            scanf("%d%d%d",&A,&B,&D);
            Insert(A,B,D,0);Insert(B,A,D,0);
        }
        int U,V,C;LL Tmp;Ans=0;
        Fo(i,1,M){
            scanf("%d%d%d",&U,&V,&C);
            Ans+=C;Insert(U,V,1,C);Insert(V,U,0,-C);
            Du[U]++;Du[V]--;
        }
        Fo(i,1,N)if(Du[i]>0)Insert(0,i,Du[i],0),Insert(i,0,0,0);
            else if(Du[i]<0)Insert(i,N+1,-Du[i],0),Insert(N+1,i,0,0);
        do{
            Fo(i,0,N+1)Vis[i]=0;
            while(Tmp=Add(0,N+1,Inf,0)){
                Fo(i,0,N+1)Vis[i]=0;
            }
        }while(Updis());
        printf("%lld\n",Ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值