【luogu P5192】Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流(网络流)

Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流

题目链接:luogu P5192

题目大意

有 m 个人,每个人有一个总需求值。
然后有 n 天,每天有一些指定的人可以给予需求,每个人可以给定的量是一个区间中的任意一个数,然后限制你每天给的所有人的总量不能超过一个给出的值。
然后问你最多能给多少,如果不能满足要求则输出 -1。

思路

首先我们考虑怎么网络流建图。
你会发现它这个给的量是一个区间感觉每条边的流量最小就不一定是 \(0\) 了,那我们先假设我们会怎么做(有源汇上下界最大流)。
那不难得出建图方法:
每天每个点建点,分别是 \(P_i,Q_i\)
每个人有总需求,\(Q_i\)\(T\) 连边,流量是 \(G_i\sim \inf\)
每天有上限,\(S\)\(P_i\) 连边,流量是 \(0\sim D_i\)
然后第 \(i\) 天,每个人 \(T_{i,j}\) 给分配,\(P_i\) 连向 \(Q_{T_{i,j}}\),流量是 \(L_{i,j}\sim R_{i,j}\)

于是问题就变成了如何求有源汇上下界最大流,这题也就是这个东西的模板了。


在求这个东西之前,让我们先从简单的来。

无源汇上下界可行流

一个图中有一些边有流量上下界,然后问你是否存在一个方案使得流量平衡且每条边的流量满足上下界的限制。
(流量平衡就是每个点流入的流量等于流出的流量)

然后有一个贪心的想法:我们把上限减去下线,然后直接跑最大流。
这个想法显然是不行的:
假如有一个 \(10\sim 20\) 的边,然后后面跟着一个 \(30\sim 40\) 的边,它们显然是流不了的。
但是减了之后就变成了两个都是 \(10\),就可以流了。

那我们考虑如何处理这个问题,亦或者说如何处理下界删掉的边。
我们其实可以发现问题是这样每个点就可能不满足流量平衡。
那我们其实会想到有一个东西是无所谓流量平衡的。
没错,就是源点汇点。
所以我们可以把那些少了的流量用源汇点来搞。

具体来讲记录入边少了的流量 \(ru_i\) 和出边的 \(chu_i\)
然后因为如果都有其实可以消掉,所以可以这样:
\(ru_i>chu_i\):从源点向这个点连流量为 \(ru_i-chu_i\) 的边。
\(ru_i=chu_i\):不用管
\(ru_i<chu_i\):从这个点向汇点连流量为 \(chu_i-ru_i\) 的边。

然后就直接跑最大流,至于判断是否可能我们可以直接看跟源点汇点相连的,因为它们是下限里面的,所以它们一定要跑满,所以就检查是否跑满即可。
(就是看最大流是否等于跟源点连的边的流量和,源汇点等价看一个即可)

有源汇上下界可行流

考虑有源汇点怎么办。
也就是说又来了两个无所谓流量平衡的点,那就不行了。
但是它们两个一个流出的量是等于一个流入的量的,所以其实可以这样让他们变成流量平衡的:汇点往源点连一条 \(\inf\) 的边。
然后就当无源汇上下界可行流来做即可。

那这个时候流量其实就可以反映为这条汇点到源点的边的反边在跑了最大流之后的流量。

有源汇上下界最大流

那你求出来的不一定是最大流,毕竟你只是保证了底线,它图里面那些可能还可以增。

那我们假设前面的流量是 \(ans1\),那我们考虑求出 \(ans2\)\(ans1\) 还能往上浮动的量。
那至于求 \(ans2\),其实不难看出因为你是原图中的边没有流满,你就直接在原图中跑一遍源点到汇点的最大流,就是 \(ans2\) 了。

那至此,这道题也就可以做了。

EX:有源汇上下界最小流

其实没什么差别,就是现在是要求一个 \(ans2\) 为还能往下浮动的量。
然后答案就是 \(ans1-ans2\)
那你不难想出这个 \(ans2\) 的求法就是跑原图汇点源点的最大流。

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#define INF 0x3f3f3f3f3f3f3f3f

using namespace std;

const int N = 365 + 10;
const int M = 1000 + 10;
struct node {
	int x, to, nxt, op;
}e[(M + N + N * 300 + 10000) << 1];
int n, m, g[M], S, T, le[N + M + 10], tot, KK;
int ru[N + M + 10], chu[N + M + 10], s1, s2, t1, t2;
int dis[N + M + 10], lee[N + M + 10];

void Add(int x, int y, int z) {
	e[++KK] = (node){z, y, le[x], KK + 1}; le[x] = KK;
	e[++KK] = (node){0, x, le[y], KK - 1}; le[y] = KK; 
}

bool bfs() {
	memcpy(lee, le, sizeof(le));
	memset(dis, 0x7f, sizeof(dis));
	queue <int> q;
	q.push(S); dis[S] = 0;
	while (!q.empty()) {
		int now = q.front(); q.pop();
		for (int i = le[now]; i; i = e[i].nxt)
			if (e[i].x && dis[e[i].to] == dis[0]) {
				dis[e[i].to] = dis[now] + 1;
				if (e[i].to == T) return 1;
				q.push(e[i].to);
			}
	}
	return 0;
}

int dfs(int now, int sum) {
	if (now == T) return sum;
	int go = 0;
	for (int &i = lee[now]; i; i = e[i].nxt)
		if (e[i].x && dis[e[i].to] == dis[now] + 1) {
			int this_go = dfs(e[i].to, min(sum - go, e[i].x));
			if (this_go) {
				e[i].x -= this_go; e[e[i].op].x += this_go;
				go += this_go; if (go == sum) return go;
			}
		}
	if (go != sum) dis[now] = -1;
	return go;
}

int Dinic() {
	int re = 0;
	while (bfs())
		re += dfs(S, INF);
	return re;
}

int main() {
	while (scanf("%d %d", &n, &m) != EOF) {
		memset(le, 0, sizeof(le)); KK = 0; int sum = 0;
		tot = n + m; s1 = ++tot; t1 = ++tot; s2 = ++tot; t2 = ++tot;
		memset(ru, 0, sizeof(ru));
		memset(chu, 0, sizeof(chu));
		
		for (int i = 1; i <= m; i++) {
			scanf("%d", &g[i]);
			Add(n + i, t1, INF - g[i]);
			chu[n + i] += g[i]; ru[t1] += g[i];
		}
		for (int i = 1; i <= n; i++) {
			int c, d, t, l, r;
			scanf("%d %d", &c, &d);
			Add(s1, i, d - 0);
			for (int j = 1; j <= c; j++) {
				scanf("%d %d %d", &t, &l, &r); t++;
				Add(i, n + t, r - l);
				chu[i] += l; ru[n + t] += l;
			}
		}
		for (int i = 1; i <= t1; i++) {//这里可以这么理解,你ru和chu分别是入边和出边省略的流量,那都有的可以抵消,但是没有的话你要通过跟源汇点连接来表示,所以是哪边多连向哪边
			if (ru[i] < chu[i]) sum += chu[i] - ru[i], Add(i, t2, chu[i] - ru[i]);
				else if (ru[i] > chu[i]) Add(s2, i, ru[i] - chu[i]);
		}
		Add(t1, s1, INF);
		S = s2; T = t2;
		if (Dinic() != sum) {
			printf("-1\n\n"); continue;
		}
		int ans = e[KK].x;
		e[KK].x = e[e[KK].op].x = 0;
		S = s1; T = t1;
		ans += Dinic();
		printf("%d\n\n", ans);
	}
	
	return 0;
}
posted @ 2022-03-15 21:10  あおいSakura  阅读(31)  评论(0编辑  收藏  举报