• [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('
    ');
    }
  • 相关阅读:
    李超线段树 [Heoi2013]Segment
    [置顶] 九月半集训总结
    [置顶] 我想学学
    图论+前缀和 任(duty)
    模拟 飞(fly)
    入坑 可持久化线段树——主席树
    一次爆炸的联考
    HASH+平衡树 [JSOI2008]火星人prefix
    乱搞+STL平衡树 序列
    数学+图论 建造游乐场
  • 原文地址:https://www.cnblogs.com/ylwtsq/p/13296205.html
Copyright © 2020-2023  润新知