题解

比较巧妙的费用流

修改流量与容量使得一张图满足流守恒与流量小于容量的条件

我们先来解决第二个问题:流量小于容量

发现只需要对c<f的边加上f-c的代价就可以了(直接修改容量c)

 

再来解决流守恒的条件

我们利用上下界网络流固定原图每条边的流量

现在只需要加上一些额外的修改边,再跑一遍上下界网络流,这个图就可以自动满足流守恒的条件

对于一条边u,v,c,f

当c<f时

由于我们预支了f-c的代价,使得c'=f

如果我们想要在现在减小流量,我们也可以回退容量的代价,使得他们两个抵消

但是如果流量减小得比以前的容量还小,那么我们预支的费用就不够抵消减小流量的代价了

所以我们还要再支出费用,单价为1

如果我们想要增大流量,我们现在的c'=f

由于要维持流量小于容量的条件,我们加一个单位的流量就还得加一个单位的容量,单价为2

 

用网络流的语言来翻译上面的话就是(adde(u,v,l,r,cost)表示加一条u->v上下界为[l,r],费用为cost的边)

adde(v,u,0,f-c,0)    (减小流量相当于在退流,所以是反向连边)

adde(v,u,0,c,1)

adde(u,v,0,inf,2)

 

当c>=f时

此时不用预支费用,但是修改流量还是有花费的

我们最多可以减小f个流量,费用为1(adde(v,u,0,f,1))

可以在小于等于c的范围内增加流量,费用也为1(adde(u,v,0,c-f,1))

如果超出了c的范围,此时我们还得同时修改容量来满足条件2,费用为2(adde(u,v,0,inf,2))

 

最后跑一遍上下界最小费用可行流就可以了

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define N 505
#define M 100005
#define LL long long
int fir[N],cur[N],to[M],nxt[M],cap[M],cst[M],cnt;
void addedge(int a,int b,int c,int d)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;cap[cnt]=c;cst[cnt]=d;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;cap[cnt]=0;cst[cnt]=-d;
}
int S,T,SS,TT,sz;
int deg[N];
LL mic,flow,pc;
void adde(int a,int b,int l,int r,int d)
{
	if(l)deg[b]+=l,deg[a]-=l,pc+=1ll*d*l;
	addedge(a,b,r-l,d);
}
int dis[N];bool vis[N];
queue<int> q;bool inq[N];
const int INF=0x3f3f3f3f;
bool spfa()
{
	for(int i=1;i<=sz;i++)dis[i]=INF;
	q.push(TT);inq[TT]=1;dis[TT]=0;
	while(!q.empty()){
		int u=q.front();q.pop();inq[u]=0;
		for(int v,w,p=fir[u];p;p=nxt[p]){
			v=to[p];w=cst[p^1];
			if(cap[p^1]>0&&dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				if(!inq[v])q.push(v),inq[v]=1;
			}
		}
	}
	return dis[SS]!=INF;
}
LL sap(int u,LL aug)
{
	if(u==TT){mic+=aug*dis[SS];return aug;}
	int tmp,ret=0;
	vis[u]=1;
	for(int v,&p=cur[u];p;p=nxt[p]){
		v=to[p];
		if(!vis[v]&&cap[p]>0&&dis[u]==dis[v]+cst[p]){
			tmp=sap(v,min(aug,1ll*cap[p]));
			cap[p]-=tmp;aug-=tmp;
			cap[p^1]+=tmp;ret+=tmp;
			if(aug==0)break;
		}
	}
	vis[u]=0;
	return ret;
}
void micflow()
{
	while(spfa()){
		for(int i=1;i<=sz;i++)cur[i]=fir[i];
		flow+=sap(SS,INF);
	}
}
int main()
{
	cnt=1;
	int n,m,i,u,v,c,f;
	scanf("%d%d",&n,&m);
	S=1;T=n;SS=n+1;TT=n+2;
	sz=TT;
	for(i=1;i<=m;i++){
		scanf("%d%d%d%d",&u,&v,&c,&f);
		adde(u,v,f,f,0);
		if(c<f){
			mic+=f-c;
			adde(v,u,0,f-c,0);
			adde(v,u,0,c,1);
			adde(u,v,0,INF,2);
		}
		else{
			adde(u,v,0,c-f,1);
			adde(u,v,0,INF,2);
			adde(v,u,0,f,1);
		}
	}
	adde(T,S,0,INF,0);
	for(i=1;i<=n;i++){
		if(deg[i]>0)addedge(SS,i,deg[i],0);
		else if(deg[i]<0)addedge(i,TT,-deg[i],0);
	}
	micflow();
	printf("%lld\n",mic);
}