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