纳克萨玛斯「GDOI2007」(网络流)

首先不考虑 (D) 的要求,可以想到一个网络流的建图模型:每个人当成一个点,每种角色也当成一个点,每个人能当某种角色就连一条容量是无穷大的边,每个人再限制只能流1的流量进来。但是这样跑不出答案,发现对于一种可行的流最后的答案是所有角色流量的最小值,那么我们可以考虑二分答案,建立一个汇点,将每种角色连向这个汇点一条容量为二分值的边,最后如果跑满了那么当前二分之一就是可行的。

考虑 (D) 的要求,对于两个正营再各新建一个点,考虑源点连向这两个点的容量。假设两点的流量为 (x,y),那么 (x+y=m*now,|x-y|le DRightarrow x,yle frac{m*now+D}{2}),其中 (now) 是当前二分的答案。把容量设为 (frac{m*now+D}{2}) 即可。

但是这样复杂度不优,无法通过此题。

我们发现每个人跟他的名字(编号)无关,之和他能当什么角色有关,所以我们考虑将他们按角色这样分类,这样人数就从 (10^4) 降到了 (10^3) 级别了,可以通过此题。

另外我还发现数据的漏洞,如果从 (n/m) 像下枚举答案会跑得更快

using namespace std;
const int MAXN = 200000;
template <typename T>
void read(T &x) {
	T flag = 1;
	char ch = getchar();
	for (; '0' > ch || ch > '9'; ch = getchar()) if (ch == '-') flag = -1;
	for (x = 0; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10+ ch - '0';
	x *= flag;
}
struct node{
	int pre, to, val;
}edge[MAXN];
int head[MAXN], tot = 1;
int n, m, D, state[MAXN], S, T, v1, v2, num, cnt[2][1 << 11], id[2][1 << 11], d[MAXN], cur[MAXN];
queue<int> q;
bool bfs(int s, int t) {
	for (int i = 0; i <= num; i++) d[i] = 0, cur[i] = head[i];
	d[s] = 1;
	q.push(s);
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		for (int i = head[x]; i; i = edge[i].pre) {
			int y = edge[i].to;
			if (edge[i].val > 0 && !d[y]) d[y] = d[x] + 1, q.push(y);
		}
	}
	return d[t] > 0;
}
int dinic(int x, int t, int flow) {
	if (x == t || flow == 0) return flow;
	int rest = flow;
	for (int &i = cur[x]; i && rest > 0; i = edge[i].pre) {
		int y = edge[i].to;
		if (edge[i].val > 0 && d[y] == d[x] + 1) {
			int k = dinic(y, t, min(rest, edge[i].val));
			if (!k) d[y] = 0;
			rest -= k; edge[i].val -= k; edge[i ^ 1].val += k;
		}
	}
	return flow - rest;
}
int maxflow(int s, int t) {
	int ret = 0, flow;
	while (bfs(s, t))
		while ((flow = dinic(s, t, 0x7f7f7f7f)) > 0)
			ret += flow;
	return ret;
}
void add_edge(int u, int v, int l) {
	edge[++tot] = node{head[u], v, l}; head[u] = tot;
	edge[++tot] = node{head[v], u, 0}; head[v] = tot;
}
void change_edge(int l) {
	edge[++tot].val = l;
	edge[++tot].val = 0;
}
int pos[12];
void build(int ans) {
	memset(head, 0, sizeof(head));
	tot = 1;
	add_edge(S, v1, (ans * m + D) >> 1);
	add_edge(S, v2, (ans * m + D) >> 1);
	for (int i = 1; i < (1 << m); i++) add_edge(v1, id[0][i], cnt[0][i]);
	for (int i = 1; i < (1 << m); i++) add_edge(v2, id[1][i], cnt[1][i]);
	for (int j = 0; j < m; j++)
		for (int i = 1; i < (1 << m); i++)
			if ((i >> j) & 1) add_edge(id[0][i], pos[j], 0x7f7f7f7f), add_edge(id[1][i], pos[j], 0x7f7f7f7f);
	for (int j = 0; j < m; j++) add_edge(pos[j], T, ans);
}
void change(int ans) {
	tot = 1;
	change_edge((ans * m + D) >> 1);
	change_edge((ans * m + D) >> 1);
	for (int i = 1; i < (1 << m); i++) change_edge(cnt[0][i]);
	for (int i = 1; i < (1 << m); i++) change_edge(cnt[1][i]);
	for (int j = 0; j < m; j++)
		for (int i = 1; i < (1 << m); i++)
			if ((i >> j) & 1) change_edge(0x7f7f7f7f), change_edge(0x7f7f7f7f);
	for (int j = 0; j < m; j++) change_edge(ans);
}
bool check(int ans) {
	change(ans);
	return maxflow(S, T) >= m * ans;
}
char s[15];
int main() {
	read(n); read(m); read(D);
	S = 0;
	v1 = ++num, v2 = ++num;
	for (int i = 1; i <= n; i++) {
		scanf("%s", s);
		for (int j = 0; j < m; j++) state[i] |= (1 << j) * (s[j] - '0');
		cnt[s[m] - '0'][state[i]]++;
	}
	for (int i = 1; i < (1 << m); i++) id[0][i] = ++num;
	for (int i = 1; i < (1 << m); i++) id[1][i] = ++num;
	for (int j = 0; j < m; j++) pos[j] = ++num;
	T = ++num;
	build(0);
	for (int i = n / m; i; i--) {
		if (check(i)) {
			printf("%d
", i);
			break;
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/zcr-blog/p/14349011.html