[ZJOI2010]网络扩容

神题++

题目大意:

 给你一个有n个节点的无向图,1为源点,n为汇点,求最大流和将最大流扩容goal个单位所需最小费用.

解题思路:

 此题思路较为灵活,不失为网络流好题.

 最大流明显板子,先求出最大流maxflow.由于最大流扩容需要费用,我们不妨新建m对边,流量为正无穷,费用为w[i],流量为inf,那接下来需要思考的唯一问题就是如何将最大流变为maxflow+goal.类比拆点操作,我们不妨新建一个汇点,与原汇点相连,流量为maxflow+goal,费用为0,第二问题就完全简化为费用流了.

code:

    

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define R register
#define next mlgvegetable
#define debug puts("mlg")
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
inline ll read();
inline void write(ll x);
inline void writesp(ll x);
inline void writeln(ll x);
ll n,m,S,T;
ll tot=1,head[200200],to[200200],next[200200],w[200200],c[200200];
inline void add(ll x,ll y,ll z,ll zz){
    to[++tot]=y;next[tot]=head[x];head[x]=tot;c[tot]=z;w[tot]=zz;
}
ll d[200200];
queue<ll>q;
bool bfs(){
    while(q.size())q.pop();
    memset(d,0,sizeof d);
    d[S]=1;q.push(S);
    while(q.size()){
        ll x=q.front();q.pop();
        for(R ll i=head[x],ver;i;i=next[i]){
            ver=to[i];
            if(!d[ver]&&c[i]){
                d[ver]=d[x]+1;
                q.push(ver);
                if(ver==T) return true;
            }
        }
    }
    return false;
}
inline ll dinic(ll x,ll flow){
    if(x==T||!flow) return flow;
    ll k,rest=flow;
    for(R ll i=head[x],ver;i&&rest;i=next[i]){
        ver=to[i];
        if(d[ver]==d[x]+1&&c[i]){
            k=dinic(ver,min(rest,c[i]));
            if(!k) d[ver]=0;
            c[i]-=k,c[i^1]+=k,rest-=k;
        }
    }
    return flow-rest;
}
ll goal;
ll incf[200200],vis[200200];
ll pre[200200],last[200200];
bool spfa(){
    while(q.size())q.pop();
    memset(d,0x3f,sizeof d);memset(vis,0,sizeof vis);
    d[S]=0;
    pre[T]=-1;
    q.push(S);
    incf[S]=1234567892020;
    while(q.size()){
        ll x=q.front();q.pop();vis[x]=0;
        for(R ll i=head[x],ver;i;i=next[i]){
            ver=to[i];
            if(!c[i]) continue;
            if(d[ver]>d[x]+w[i]){
                d[ver]=d[x]+w[i];
                pre[ver]=x;last[ver]=i;
                incf[ver]=min(incf[x],c[i]);
                last[ver]=i;
                if(!vis[ver]){
                    q.push(ver);
                    vis[ver]=true;
                }
            }
        }
    }
    return pre[T]!=-1;
}
ll v[200200],xx[200200],yy[200200],u[200200];
ll pay;
inline void update(){
    ll x=T;
    while(x!=S){
        c[last[x]]-=incf[T];
        c[last[x]^1]+=incf[T];
        x=pre[x];
    }
    pay+=d[T]*incf[T];
}
int main(){
    n=read();m=read();goal=read();S=1;T=n;
    for(R ll i=1,x,y;i<=m;i++){
        xx[i]=x=read();yy[i]=y=read();u[i]=read();v[i]=read();
        add(x,y,u[i],0);add(y,x,0,0);
    }
    ll ans=0,_flow;
    while(bfs()){
        while(_flow=dinic(S,0x7fffffff)) ans+=_flow;
    }
    writesp(ans);
    for(R ll i=2;i<=tot;i+=2){
        c[i]+=c[i^1];c[i^1]=0;
    }
        /*
           由于此时我最大流已经求过,而点n到n+1还未流过,所以需重置流量,保证图的合法性.
        */
    for(R ll i=1;i<=m;i++){
        add(xx[i],yy[i],0x7fffffff,v[i]);add(yy[i],xx[i],0,-v[i]);
    }
    T=n+1;add(n,n+1,ans+goal,0);add(n+1,n,0,0);
    while(spfa()) update();
    writeln(pay);
}

inline ll read(){
    ll x=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') t=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*t;
}
inline void write(ll x){
    if(x<0){putchar('-');x=-x;}
    if(x<=9){putchar(x+'0');return;}
    write(x/10);putchar(x%10+'0');
}
inline void writesp(ll x){
    write(x);putchar(' ');
}
inline void writeln(ll x){
    write(x);putchar('\n');
}

 

posted @ 2020-07-13 22:11  月落乌啼算钱  阅读(155)  评论(0编辑  收藏  举报