Gym 102007I 二分 网络流

题意:给你一张图,每个城市有一些人,有不超过10个城市有避难所,避难所有容量上限,问最快多久可以让所有人进入避难所?

思路:二分时间,对于每个时间跑一遍最大流,判断最大流是不是人数即可。我们还需要用二进制优化一下,对于每个二分的时间,我们需要预处理出某个城市可以到达哪些避难所,表示成状态。如果在当前时间下两个城市可以到达的避难所是一样的,我们就可以用一个状态表示,这样把点数限制在了1e3级别。

代码:

#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
#define LL long long
#define pLi pair<LL ,int>
#define pii pair<int, int>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
using namespace __gnu_pbds;
const int maxn = 200100;
const int maxm = 501000;
typedef __gnu_pbds::priority_queue<pLi, greater<pLi>, pairing_heap_tag> Heap;
int heade[maxn],
 Nexte[maxm], vere[maxm], edgee[maxm], tote;
LL dis[10][maxn];
pair<int, int> b[20];
int n, k;
int a[maxn];
LL re[maxn * 10];
void adde(int x, int y, int z) {
	vere[++tote] = y;
	edgee[tote] = z;
	Nexte[tote] = heade[x];
	heade[x] = tote;
}
Heap q;
int s, t;
void dijkstra(int s, int flag) {
	memset(dis[flag], 0x3f, sizeof(dis[flag]));
	Heap::point_iterator id[maxn];
	dis[flag][s] = 0;
	id[s] = q.push(make_pair(dis[flag][s], s));
	while(!q.empty()) {
		int x = q.top().second;
		q.pop();
		for (int i = heade[x]; i; i = Nexte[i]) {
			int y = vere[i], z = edgee[i];
			if(dis[flag][y] > dis[flag][x] + z) {
				dis[flag][y] = dis[flag][x] + z;
				if(id[y] != 0) q.modify(id[y], make_pair(dis[flag][y], y));
				else id[y] = q.push(make_pair(dis[flag][y], y));
			}
		}
	}
}
int head[maxn], Next[maxn * 25], ver[maxn * 25], tot;
LL edge[maxn * 25];
int deep[maxn];
void add(int x, int y, LL z) {
	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
	ver[++tot] = x, edge[tot] = 0, Next[tot] = head[y], head[y] = tot;
}
queue<int> Q;
bool bfs(LL limit) {
	memset(deep, 0, sizeof(deep));
	while(Q.size())Q.pop();
	Q.push(s); deep[s] = 1;
	while(Q.size()) {
		int x = Q.front();Q.pop();
		for (int i = head[x]; i; i = Next[i]) {
			if(edge[i] && !deep[ver[i]]) {
				Q.push(ver[i]);
				deep[ver[i]] = deep[x] + 1;
				if(ver[i] == t) return 1;
			}
		}
	}
	return 0;
}
LL dinic(int x, LL flow) {
	if(x == t) return flow;
	LL rest = flow, k;
	for (int i = head[x]; i && rest; i = Next[i]) {
		if(edge[i] && deep[ver[i]] == deep[x] + 1) {
			k = dinic(ver[i], min(rest, edge[i]));
			if(!k) deep[ver[i]] = 0;
			edge[i] -= k;
			edge[i ^ 1] += k;
			rest -= k;
		}
	}
	return flow - rest;
}
LL solve(int now) {
	tot = 1;
	LL maxflow = 0;
//	memset(head, 0, sizeof(head));
	memset(head, 0, sizeof(int) * (n + 1));
	for (int i = 0; i < k; i++)
		head[b[i].first + n] = 0;
	head[t] = 0;
	for (int i = 1; i <= n; i++) {
		if(a[i])
			add(s, i, a[i]);
	}
	LL tmp;
	for (int i = 1; i <= n; i++) {
		tmp = 0;
		if(a[i] == 0) continue;
		for (int j = 0; j < k; j++) {
			if(dis[j][i] >= re[now]) continue;
			tmp += b[j].second;
			add(i, b[j].first + n, INF);
		}
		if(tmp < a[i]) {
			return 0;
		}
	}
	for (int i = 0; i < k; i++)
		add(b[i].first + n, t, b[i].second);
	LL flow;
	while(bfs(re[now])) {
		while(flow = dinic(s, INF)) maxflow += flow;
	}
	return maxflow;
}
int main() {
	LL cnt = 0;
	s = 0, t = 2 * n + 20;
	int m, x, y, z;
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 1; i <= n; i++)  {
		scanf("%d", &a[i]);
		cnt += a[i];
	}
	for (int i = 1; i <= m; i++) {
		scanf("%d%d%d", &x, &y, &z);
		adde(x, y, z);
		adde(y, x, z); 
	}
	for (int i = 0; i < k; i++) {
		scanf("%d%d", &b[i].first, &b[i].second);
		dijkstra(b[i].first, i);
	}
	for (int i = 0; i < k; i++)
		for (int j = 1; j <= n; j++) {
			re[++tot] = dis[i][j];
		}
	sort(re + 1, re + 1 + tot);
	int sz = unique(re + 1, re + 1 + tot) - (re + 1);
	int l = 1, r = sz + 1;
	re[sz + 1] = INF;
	while(l < r) {
		int mid = (l + r) >> 1;
		if(solve(mid) == cnt) r = mid;
		else l = mid + 1;
	}
	printf("%lld\n", re[l - 1]);
}

  

posted @ 2019-05-15 18:19  维和战艇机  阅读(286)  评论(0编辑  收藏  举报