【题解】CF212A Privatization
CF212A Privatization
题意简述
给定 \(n+m\) 个点 \(k\) 条边的二分图,左边 \(n\) 个点,右面 \(m\) 个点。现在要把每条边标号,标号为 \(1\sim t\)。求出 对于每个点与之相邻的边中最多的标号与最少标号的数量差 的平均值。\(n,m,t \le 200, k \le 5000\)。
题解
先上结论:
答案就是度数不为 \(t\) 的倍数的点的个数除以 \(n+m\)。
首先,答案肯定不大于这个值(显然,如果度数不为 \(t\) 的倍数那么 \(\max - \min\) 至少为 \(1\))。
然后考虑扔掉一些边使得每个点的度数都是 \(t\) 的倍数。问题转化为给这个新的图标号使得每个点的标号数量都一致。
对于一个度数为 \(d_i\) 的点(假设 \(t|d_i\)),我们应该把 \(d_i\) 条边分成 \(d_i/t\) 个块,每个块都有 \(t\) 种标号各一个。
然后我们考虑如果新增一条边 \((u,v)\),那么我们应该给这条边标上怎样的编号。
显然在 \(u\) 的当前块中肯定至少还有一种标号没有使用。如果有多个标号没有使用,则我们可以使用任意一种。
如果这个标号在 \(v\) 中也可用那么我们显然可以直接给这条边标上这个标号。但是如果 \(u\) 当前块中所有可用的颜色在 \(v\) 当前块中都用过了,那么我们怎么办呢?
其实已经标过号的边是可以换标号的。假设现在选取的标号是 \(t_1\),令 \(v\) 中的任意一个未用的标号为 \(t_2\)。则我们可以把原来标号是 \(t_1\) 的换成 \(t_2\),如果产生冲突则继续调整(“交换”标号为 \(t_1\) 和 \(t_2\) 的边)。
当然,我们也能用这个结论建图网络流来构造。
#include <bits/stdc++.h>
const int N = 405;
const int M = 1e4 + 5;
int n, m, k, t, res[M], deg[N], id[N], tot, u[M], v[M];
int f[M * 5][N];
inline int find(int x) {
for (int i = 1; i <= t; ++i) if (!f[x][i]) return i;
}
void flip(int x, int s, int t) {
int e = f[x][s], y = (x == u[e]) ? v[e] : u[e];
f[x][s] = f[y][s] = 0;
if (f[y][t]) flip(y, t, s);
f[x][t] = f[y][t] = e, res[e] = t;
}
int main() {
scanf("%d%d%d%d", &n, &m, &k, &t);
for (int i = 1; i <= k; ++i) {
scanf("%d%d", u + i, v + i); v[i] += n;
if (deg[u[i]] % t == 0) id[u[i]] = ++tot;
if (deg[v[i]] % t == 0) id[v[i]] = ++tot;
++deg[u[i]], ++deg[v[i]];
u[i] = id[u[i]], v[i] = id[v[i]];
int t1 = find(u[i]), t2 = find(v[i]);
if (t1 == t2) f[u[i]][t1] = f[v[i]][t1] = i, res[i] = t1;
else {
if (f[u[i]][t2]) flip(u[i], t2, t1);
f[u[i]][t2] = f[v[i]][t2] = i, res[i] = t2;
}
}
int ans = 0;
for (int i = 1; i <= n + m; ++i) if (deg[i] % t) ++ans;
printf("%d\n", ans);
for (int i = 1; i <= k; ++i) printf("%d ", res[i]);
printf("\n");
return 0;
}