【网络流】CF708D Incorrect Flow

思考过程。无脑记录。

流量守恒:记 \(d(x)=in_x-out_x,d(x)=0\),因为上下界网络流经常这么表示,当然记 \(d(x)=out_x-in_x\) 也行。

减容量是可以舍弃的操作,因为减容量总是可以使用减流量解决或者会使其更不优。

再者,每条边给定的原始流量一定要先流满再说,所以上下界都是 \(f\)。

下面都省略了下界 0。

\(f \le c\),那么在不增加 \(c\) 的情况下,\(f\) 还能加 \(c-f\),考虑对 \(d(u),d(v)\) 的影响,假如加了 \(l\),那么 \(d(u)-=l,d(v)+=l\)。考虑这个式子如何建模吧。若原来的 \(d(x)>0,x\in [1,n]\) 说明入流多,要流出。对应的,对 \(d(x)<0\) 的,要流入,这部分有助于建模思考。考虑加 1,那么就是 \((u,v,c-f,1)\),即假如需要流经这条边,则会给 \(u\) 提供流出流量,给 \(v\) 提供流入流量。考虑减 1,那么就是对称的 \((v,u,f,1)\)。再考虑下当 \(f=c\) 的时候,这时候我们要增加流量的话,只能加容量再加流量,即花费 2。那么就是 \((u,v,+\infty,2)\)。

\(f>c\),欸,我不会,睡觉了。

考虑增加流量,那么先要增加 \(f-c\) 的容量(这东西我还没想明白咋建模,主要是这个花费应该只能后面跑完在算吧),且后面每次增加流量都要增加容量,即 \((u,v,+\infty,2)\)。减少流量,那么先要减少 \(f-c\) 的流量,再 \((v,u,c,1)\)。好像漏了些,不懂。。。

总算明白了,\(c\le f'\le f\) 也算一种情况,那么 \((f-f')+(f'-c)=f-c\),花费始终是 \(f-c\) 的,所以无论如何 \(f>c\) 的情况都有一个固定原始花费。那么这样子算减少流量,即 \((v,u,f-c,0)\),即最多可以减少这么多,因为只要保证 \(c\le f'\le f\) 即可。

#include <bits/stdc++.h>
#define int long long
#define inf (int)(2e15)
using namespace std;
int rd() {
    int f=1,sum=0; char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return sum*f;
}
#define N (int)(2e5+5)
struct edge {
	int nex,to,w,c;
}e[N<<1];
int chea[N],hea[N],d[N],cnt=1,n,m,ans,S,T,s,t;

void add_edge(int x,int y,int z,int c) {
	e[++cnt].nex=hea[x]; e[cnt].to=y; e[cnt].w=z; e[cnt].c=c; hea[x]=cnt;
}

void add(int x,int y,int z,int c) {
	add_edge(x,y,z,c); add_edge(y,x,0,-c);
}

void lradd(int x,int y,int l,int r,int c) {
	add(x,y,r-l,c); d[x]-=l; d[y]+=l;
}

namespace WLL {
	queue<int>q;
	int dis[N],S,T;
	bool vis[N];
	bool spfa() {
		for(int i=0;i<=T;i++) dis[i]=inf,vis[i]=0;
		dis[S]=0; q.push(S);
		while(!q.empty()) {
			int x=q.front(); q.pop();
			vis[x]=0;
			for(int i=hea[x];i;i=e[i].nex) {
				int y=e[i].to;
				if(e[i].w&&dis[y]>dis[x]+e[i].c) {
					dis[y]=dis[x]+e[i].c;
					if(!vis[y]) vis[y]=1,q.push(y);
				}
			}
		}
		return dis[T]<inf;
	}
	int dfs(int x,int lim) {
		if(x==T||!lim) return lim;
		int flow=0,fl;
		vis[x]=1;
		for(int i=hea[x];i&&lim;i=e[i].nex) {
			int y=e[i].to;
			if(e[i].w&&!vis[y]&&dis[y]==dis[x]+e[i].c) {
				fl=dfs(y,min(lim,e[i].w));
				if(!fl) continue;
				flow+=fl; lim-=fl; e[i].w-=fl; e[i^1].w+=fl;
				ans+=e[i].c*fl;
			}
		}
		if(!flow) dis[x]=inf;
		vis[x]=0;
		return flow; 
	}
	void zkw(int ss,int tt) {
		S=ss; T=tt;
		while(spfa()) {
			memset(vis,0,sizeof(int)*(T+2));
			dfs(S,inf);
		}
	}
}

signed main() {
	n=rd(); m=rd(); s=1; t=n; S=0; T=n+1;
	for(int i=1;i<=m;i++) {
		int x=rd(),y=rd(),c=rd(),f=rd();
		lradd(x,y,f,f,0);
		if(f<=c) {
			lradd(x,y,0,c-f,1);
			lradd(y,x,0,f,1);
			lradd(x,y,0,inf,2);
		} else {
			ans+=f-c;
			lradd(x,y,0,inf,2);
			lradd(y,x,0,c,1);
			lradd(y,x,0,f-c,0);
		}
	}
	lradd(t,s,0,inf,0);
	for(int i=1;i<=n;i++) {
		if(d[i]>0) add(S,i,d[i],0);
		else if(d[i]<0) add(i,T,-d[i],0);
	}
	WLL::zkw(S,T);
	printf("%lld",ans);
	return 0;
}
上一篇:4软件程序运行


下一篇:树上问题乱做