← 全部文章

CF708D Incorrect Flow 题解

本文目录(3 节)
  1. 题意
  2. 题解
  3. 代码

CF708D Incorrect Flow

题意

  • 给定一张 nn 个点 mm 条边的网络,源点为 11,汇点为 nn。
  • 对于每条边 (u,v)(u,v),有容量 cc,当前流量 ff。
  • 但这个图是错误的,可能存在 c<fc < f,或者流量不守恒的情况。
  • 你每次操作可以将某条边的 cc 或 ff 加 11 或减 11。
  • 请你用最少的操作次数将图变成一个正确的网络。
  • n,m≤100n,m \le 100,c,f≤106c,f \le 10^6,11 没有入边,nn 没有出边。

题解

建立新的网络,下面用 (x,y,z,w)(x,y,z,w) 表示一条 x→yx \to y,容量为 zz,费用为 ww 的边。

首先考虑处理每一条原图中的边,设 f′f^\prime 表示操作后的流量。

对于 c≥fc \ge f 的边:

  1. 0≤f′≤f0 \le f^\prime \le f:连边 (v,u,f,1)(v, u, f, 1),表示流量每减一,花费加一,最多减 ff 次。
  2. f<f′≤cf < f^\prime \le c:连边 (u,v,c−f,1)(u, v, c - f, 1),表示流量每加一,花费加一,最多加 c−fc - f 次。
  3. f′>cf^\prime > c:连边 (u,v,+∞,2)(u, v, +\infty, 2),表示流量每加一,花费加二,可以无限加。

对于 c<fc < f 的边,由于至少会产生 f−cf - c 的花费,所以先加上:

  1. 0≤f′<c0 \le f^\prime < c:连边 (v,u,c,1)(v, u, c, 1),表示将 f−cf-c 的花费用在将流量减 f−cf-c 上,因此流量每减一,花费加一,最多减 cc 次。
  2. c≤f′≤fc \le f^\prime \le f:连边 (v,u,f−c,0)(v, u, f - c, 0),表示将 f−cf-c 的花费用在将流量减 f−f′f - f^\prime,容量加 f′−cf^\prime - c 上,因此流量每减一,花费不变,最多减 f−cf-c 次。
  3. f′>ff^\prime > f:连边 (u,v,+∞,2)(u,v,+\infty, 2),表示将 f−cf-c 的花费用在将容量加 f−cf - c 上,因此流量每加一,花费加二,可以无限加。

那原本的流量 ff 怎么办呢?

考虑把网络改为上下界网络,则对于原图中的一条边,连边 (u,v,(f,f),0)(u,v,(f,f), 0)。

然后跑有源汇上下界最小费用可行流即可。

代码

namespace EK {
	const int N = 1e4 + 7, M = 1e5 + 7;
	const ll inf = 1e18;
	int n, S, T, h[N], e[M], t[M], tot, pre[N], v[N];
	ll mxf, ans, f[M], c[M], d[N], now[N];
	inline void add(int x, int y, ll z, ll w, int o = 1) {
		e[++tot] = y, f[tot] = z, c[tot] = w, t[tot] = h[x], h[x] = tot;
		if (o) add(y, x, 0, -w, 0);
	}
	inline bool spfa() {
		for (int i = 1; i <= n; i++) v[i] = 0, d[i] = inf;
		queue<int> q;
		q.push(S), v[S] = 1, d[S] = 0, now[S] = inf;
		while (q.size()) {
			int x = q.front();
			q.pop(), v[x] = 0;
			for (int i = h[x]; i; i = t[i]) {
				int y = e[i];
				ll z = f[i], w = c[i];
				if (!z || d[y] <= d[x] + w) continue;
				d[y] = d[x] + w, now[y] = min(now[x], z), pre[y] = i;
				if (!v[y]) q.push(y), v[y] = 1;
			}
		}
		return d[T] != inf;
	}
	inline void upd() {
		mxf += now[T], ans += d[T] * now[T];
		int x = T;
		while (x != S)
			f[pre[x]] -= now[T], f[pre[x]^1] += now[T], x = e[pre[x]^1];
	}
	inline void main() {
		while (spfa()) upd();
	}
	inline void init(int _n, int _S, int _T) {
		n = _n, S = _S, T = _T, tot = 1, mxf = ans = 0;
		for (int i = 1; i <= n; i++) h[i] = 0;
	}
}

namespace no_min_cost_flow {
	const int N = 1e5 + 7;
	int n, S, T;
	ll d[N], s;
	inline void add(int x, int y, ll l, ll r, ll w) {
		EK::add(x, y, r - l, w), d[y] += l, d[x] -= l;
	}
	inline bool main() {
		for (int i = 1; i <= n; i++)
			if (d[i] > 0) EK::add(S, i, d[i], 0), s += d[i];
			else if (d[i] < 0) EK::add(i, T, -d[i], 0);
		EK::main();
		return EK::mxf == s;
	}
	inline void init(int _n) {
		n = _n, s = 0, S = n + 1, T = n + 2;
		EK::init(n + 2, S, T);
		for (int i = 1; i <= n; i++) d[i] = 0;
	}
}

const ll inf = 1e18;
ll ans;

int main() {
	int n, m, S, T;
	rd(n), rd(m), S = 1, T = n;
	no_min_cost_flow::init(n);
	no_min_cost_flow::add(T, S, 0, inf, 0);
	for (int i = 1, u, v, c, f; i <= m; i++) {
		rd(u), rd(v), rd(c), rd(f);
		no_min_cost_flow::add(u, v, f, f, 0);
		if (c >= f) {
			no_min_cost_flow::add(v, u, 0, f, 1);
			no_min_cost_flow::add(u, v, 0, c - f, 1);
			no_min_cost_flow::add(u, v, 0, inf, 2);
		} else {
			ans += f - c;
			no_min_cost_flow::add(v, u, 0, c, 1);
			no_min_cost_flow::add(v, u, 0, f - c, 0);
			no_min_cost_flow::add(u, v, 0, inf, 2);
		}
	}
	no_min_cost_flow::main();
	print(EK::ans + ans);
	return 0;
}

标签:网络流

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…