LOJ.117.[模板]有源汇有上下界最小流(Dinic)

时间:2023-03-08 21:31:33

题目链接

有源汇有上下界最小流

Sol1. 首先和无源汇网络流一样建图,求SS->TT最大流;

然后连边(T->S,[0,INF]),再求一遍SS->TT最大流,答案为新添加边的流量

无解情况: 连边后再求最大流+之前的最大流 != ∑dgr

解释: 第一次最大流已经满足下界,满足下界的情况下能流的边已尽量流满

那么残量网络的最大流就会尽可能小了

Sol2. 首先和无源汇网络流一样建图,然后连边(T->S,[0,INF]),求SS->TT的最大流okflow

然后删去T->S的这条边,求T->S的最大流mn,则答案为okflow-mn

解释: 第一次是求可行流,使其满足流量下界;

第二次利用 反向弧流量的增加=正向边流量的减少,由T->S的最大流就是S->T最大能减少的流量(满足可行流)

#include<cstdio>
#include<cctype>
#include<algorithm>
#define gc() getchar()
typedef long long LL;
const int N=5e4+9,M=125009+N;
const LL INF=1e14; int n,m,src,des,Enum,H[N],nxt[M<<1],to[M<<1],lev[N],cur[N],q[N],dgr[N];
LL cap[M<<1]; inline LL read()
{
LL now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline void AddEdge(int u,int v,LL w)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, cap[Enum]=w;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, cap[Enum]=0;
}
bool BFS()
{
for(int i=0;i<=n+1;++i) lev[i]=0,cur[i]=H[i];
lev[src]=1, q[0]=src;
int h=0,t=1;
while(h<t)
{
int x=q[h++];
for(int i=H[x];i;i=nxt[i])
if(!lev[to[i]]&&cap[i])
{
lev[to[i]]=lev[x]+1, q[t++]=to[i];
if(to[i]==des) return 1;
}
}
return 0;
}
LL Dinic(int u,LL flow)
{
if(u==des) return flow;
LL used=0;
for(int &i=cur[u];i;i=nxt[i])
if(lev[to[i]]==lev[u]+1 && cap[i])
{
LL delta=Dinic(to[i],std::min(cap[i],flow-used));
if(delta)
{
cap[i]-=delta, cap[i^1]+=delta, used+=delta;
if(used==flow) return flow;
}
}
lev[u]=0;
return used;
} int main()
{
Enum=1;
n=read(),m=read();int s=read(),t=read(),ss=0,tt=n+1;
LL low,upp,sum=0,okflow=0;
for(int u,v,i=1;i<=m;++i)
{
u=read(),v=read(),low=read(),upp=read(),
dgr[u]-=low,dgr[v]+=low, AddEdge(u,v,upp-low);
}
for(int i=1;i<=n;++i)
if(dgr[i]>0) AddEdge(ss,i,dgr[i]),sum+=dgr[i];
else if(dgr[i]<0) AddEdge(i,tt,-dgr[i]);
src=ss, des=tt;
while(BFS()) okflow+=Dinic(src,INF);
AddEdge(t,s,INF);
while(BFS()) okflow+=Dinic(src,INF);
if(okflow==sum) printf("%lld",cap[Enum]);
else printf("please go home to sleep"); return 0;
}