神题++
题目大意:
给你一个有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(' '); }