【NOI2017模拟.4.1】 Tree【最大费用循环流】

20 篇文章 0 订阅
7 篇文章 0 订阅

Description

这里写图片描述

Solution

这题一看就是网络流,但是时无向图,无源汇……
最大费用循环流!
首先连边是很显然的,连成一个环就可以了,树上的边连双向(上下都能走),然后要覆盖一条链,那么这条链就从下往上连(其实上面连了双向,这里从下往上也可以)
然后直接套上最小费用循环流就可以了。
首先把所有的正权边都流满为ans
然后因为要流量平衡,所以要平衡一下。
统计一下现在的流量度数:
1、如果度数>0,S向i连边,流量为度数,费用0
2、如果度数<0,i向T连边,流量为-度数,费用0
原图的边流量和费用不变
然后跑一次最小费用最大流为ans1
因为后面的流量是来平衡的,所以要用ans-ans1为最终的答案。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define rep(i,a) for(i=first[a];i;i=next[i])
#define rep1(i,a) for(i=first1[a];i;i=next1[i])
using namespace std;
typedef long long ll;
const int maxn=1e5+7,inf=0x7fffffff;
ll i,j,k,l,t,n,m,ans,cas;
ll first[maxn*2],last[maxn*2],next[maxn*2],chang[maxn*2],cost[maxn*2],d[maxn*2],num;
ll first1[maxn*2],last1[maxn*2],next1[maxn*2],chang1[maxn*2],num1,fan[maxn*2];
ll S,T,id,bz[maxn],ans1,deep[maxn],du[maxn];
bool yi,cz,lian;
struct node{
    ll u,v,c;
}a[maxn];
bool cmp(node x,node y){
    return x.u<y.u;
}
void add(ll x,ll y,ll z,ll o){
    last[++num]=y,next[num]=first[x],first[x]=num,chang[num]=z,cost[num]=o,fan[num]=num+1;
    last[++num]=x,next[num]=first[y],first[y]=num,chang[num]=0,cost[num]=-o,fan[num]=num-1;
}
void add1(ll x,ll y,ll z){
    last1[++num1]=y,next1[num1]=first1[x],first1[x]=num1,chang1[num1]=z;
}
void dfs(ll x,ll y){
    int i;
    deep[x]=deep[y]+1;
    rep1(i,x){
        if(last1[i]!=y){
            add(x,last1[i],chang1[i],0);
            add(last1[i],x,chang1[i],0);
         //   du[x]+=chang1[i],du[last1[i]]-=chang1[i];
            dfs(last1[i],x);
        }
    }
}
int zkw(ll x,ll y,ll z){
    if(!y)return 0;
    if(x==T){ans1+=y*z;return y;}
    int i,j,k=0;
    bz[x]=id;
    rep(i,x){
        if(bz[last[i]]!=id&&chang[i]&&d[x]==d[last[i]]+cost[i]){
            j=zkw(last[i],min(y,chang[i]),z+cost[i]);
            if(j){
                chang[i]-=j,chang[fan[i]]+=j;
                k+=j,y-=j;
                if(!y)break;
            }
        }
    }
    return k;
}
bool pan(){
    ll i,j,k,o=inf;
    if(!id)return 1;
    fo(i,S,T){
        if(bz[i]==id){
            rep(j,i){
                if(bz[last[j]]!=id&&chang[j])o=min(o,d[last[j]]-d[i]+cost[j]);
            }
        }
    }
    if(o==inf)return 0;
    fo(i,S,T)if(bz[i]==id)d[i]+=o;
    return 1;
}
int main(){
//  freopen("tree.in","r",stdin);
//  freopen("tree.out","w",stdout);
    freopen("fan.in","r",stdin);
    for(scanf("%d",&cas);cas;cas--){
        scanf("%d%d",&n,&m);
        ans=ans1=0;
        memset(first,0,sizeof(first));num=num1=0;memset(first1,0,sizeof(first1));
        memset(du,0,sizeof(du));memset(d,0,sizeof(d));memset(deep,0,sizeof(deep));
        memset(bz,0,sizeof(bz));
        S=0,T=n+1;
        fo(i,1,n-1){
            scanf("%d%d%d",&k,&l,&t);
            add1(k,l,t),add1(l,k,t);
        }
        dfs(1,0);
        fo(i,1,m){
            scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].c);
            if(deep[a[i].u]<deep[a[i].v])swap(a[i].u,a[i].v);
            du[a[i].u]++,du[a[i].v]--;
            ans+=a[i].c;
            add(a[i].u,a[i].v,1,a[i].c);
        }
        fo(i,1,n){
            if(du[i]>0)add(S,i,du[i],0);
            else if(du[i]<0)add(i,T,-du[i],0);
        }
        id=0;
        while(pan()){
            id++;
            while(zkw(S,inf,0))id++;
        }
        printf("%lld\n",ans-ans1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值