首先不考虑 (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;
}