bzoj 5251 [2018多省省队联测]劈配 网络流+二分答案

题面

题目传送门

解法

当年我果然还是太菜了

  • 先考虑第一问怎么解决
  • 对于每一个选手 i i ,连接 ( S , i , 1 ) (S,i,1) ;对于每一个评委 j j ,连接 ( j , T , b [ j ] ) (j,T,b[j])
  • 选手按照编号从小到大处理,枚举最后的答案,然后加上相应的边,看是否能够增广就可以了。
  • 对于第二问,可以记录一下每一个选手做完之后的残量网络,二分答案即可。

【注意事项】

  • 建议在第一问枚举答案的时候删去不需要的边,即发现 j j 不能作为答案时将第 j j 志愿相关的边全部删去,可以加快一点速度。

代码

#include <bits/stdc++.h>
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
const int N = 410, inf = 1 << 30;
int n, m, s, t, cnt, l[N], b[N], ed[N], tx[N], fr[N], cur[N], lev[N], head[N], h[N][N];
vector <int> v[N][N];
struct Edge {int pre, from, next, num, c;} e[N * N], E[210][10010];
void add(int x, int y, int c) {
	e[++cnt] = (Edge) {0, x, head[x], y, c};
	e[e[cnt].next].pre = head[x] = cnt;
}
void del(int p) {
	int x = e[p].from;
	if (p != head[x]) {
		e[e[p].pre].next = e[p].next;
		if (e[p].next) e[e[p].next].pre = e[p].pre;
	} else head[x] = e[p].next;
}
void Add(int x, int y, int c) {add(x, y, c), add(y, x, 0);}
bool bfs(int s, int t) {
	memset(l, -1, sizeof(l));
	queue <int> q; q.push(s), l[s] = 0;
	while (!q.empty()) {
		int x = q.front(); q.pop();
		for (int p = head[x]; p; p = e[p].next) {
			int k = e[p].num, c = e[p].c;
			if (l[k] == -1 && c) l[k] = l[x] + 1, q.push(k);
		}
	}
	return l[t] != -1;
}
int dfs(int x, int t, int lim) {
	if (x == t) return lim; int ret = 0;
	for (int &p = cur[x]; p; p = e[p].next) {
		int k = e[p].num, c = e[p].c;
		if (l[k] == l[x] + 1 && c) {
			int w = dfs(k, t, min(lim - ret, c));
			e[p].c -= w, e[p ^ 1].c += w, ret += w;
			if (ret == lim) return ret;
		}
	}
	if (!ret) l[x] = -1; return ret;
}
int dinic(int s, int t) {
	int ret = 0;
	while (bfs(s, t)) {
		memcpy(cur, head, sizeof(cur));
		ret += dfs(s, t, inf);
	}
	return ret;
}
bool check(int id, int rnk, int lev) {
	for (int i = s; i <= t; i++) head[i] = h[rnk - 1][i];
	for (int i = 2; i <= ed[rnk - 1]; i++) e[i] = E[rnk - 1][i];
	for (int i = 1; i <= lev; i++)
		for (int j = 0; j < v[id][i].size(); j++) Add(id, v[id][i][j] + n, 1);
	return dinic(s, t);
}
int main() {
	int T, c; read(T), read(c);
	while (T--) {
		read(n), read(m); cnt = 1;
		memset(e, 0, sizeof(e)), memset(head, 0, sizeof(head));
		for (int i = 1; i <= m; i++) read(b[i]);
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= m; j++)
				v[i][j].clear();
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= m; j++) {
				int x; read(x);
				v[i][x].push_back(j);
			}
		for (int i = 1; i <= n; i++) read(tx[i]), lev[i] = m + 1;
		s = 0, t = n + m + 1;
		for (int i = 1; i <= n; i++) Add(s, i, 1), fr[i] = cnt;
		for (int i = 1; i <= m; i++) Add(i + n, t, b[i]);
		for (int i = s; i <= t; i++) h[0][i] = head[i];
		for (int i = 1; i <= cnt; i++) E[0][i] = e[i];
		int las = cnt; ed[0] = cnt;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				for (int k = 0; k < v[i][j].size(); k++) Add(i, v[i][j][k] + n, 1);
				int tmp = dinic(s, t);
				if (e[fr[i]].c) {lev[i] = j, las = cnt; break;}
				for (int k = las + 1; k <= cnt; k++) del(k); cnt = las;
			}
			ed[i] = cnt;
			for (int j = 1; j <= cnt; j++) E[i][j] = e[j];
			for (int j = s; j <= t; j++) h[i][j] = head[j];
		}
		for (int i = 1; i <= n; i++) cout << lev[i] << ' '; cout << "\n";
		for (int i = 1; i <= n; i++) {
			if (lev[i] <= tx[i]) {cout << "0 "; continue;}
			int l = 1, r = i - 1, ans = i;
			while (l <= r) {
				int mid = (l + r) >> 1;
				if (check(i, i - mid, tx[i])) ans = mid, r = mid - 1;
					else l = mid + 1;
			}
			cout << ans << ' ';
		}
		cout << "\n";
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/87298241