题目大意.
给出一个二分图,你要给边染色,使同色边不相邻。求最小色数并构造一组方案。
……其实你不需要使色数最小,设最小色数为
,你只要构造一个色数为 的方案即可。
。 时限 6s。
首先,显然地
证明. (为什么
就是最小色数) 可见,如果我们能找出一组互不相邻的边集
,使得 中每个点的度数都 就可以根据归纳法证完了。 考虑左部点(下记为
)中达到 的点集 。 到右部点 必有一完美匹配(根据 Hall 定理,如果此结论不成立,右部必存在一个度数 的点,这是不可能的)。对右部点有类似结论。 现在考虑这两个匹配的并
。显然 中可能有相邻边,相邻边构成的连通块要么是路径要么是偶环。
- 如果是奇路径,保留较多的一半边;
- 如果是偶路径 / 偶环,任意保留一半数量的边。
不难验证,这就构造了一个合法的
。
当然,每次求两遍匹配必然是不可接受的,所以要注意利用
具体来说,一旦发现有两条边相邻于点
xxxxxxxxxx
using namespace std;
typedef pair<int, int> pii;
const int maxn = 500005;
int nL, nR, m;
int clr[maxn], idx;
struct edge { int u, v, id; };
int fa[maxn], val[maxn];
pii find(int x) {
if(fa[x] == x) return make_pair(x, 0);
pii tmp = find(fa[x]);
return make_pair(fa[x] = tmp.first, val[x] ^= tmp.second);
}
void merge(int x, int y) {
pii fx = find(x), fy = find(y);
if (fx.first == fy.first) return;
fa[fx.first] = fy.first;
val[fx.first] = (fx.second == fy.second);
}
int D[maxn];
void solve(const vector<edge> &E) {
for (edge e : E) D[e.u] = D[e.v] = -1;
for (edge e : E) fa[e.id] = e.id, val[e.id] = 0;
bool flg = 0;
for (edge e : E) {
int u = e.u, v = e.v;
if (D[u] < 0) D[u] = e.id;
else flg = 1, merge(D[u], e.id), D[u] = -1;
if (D[v] < 0) D[v] = e.id;
else flg = 1, merge(D[v], e.id), D[v] = -1;
}
if (!flg) {
idx++;
for (edge e : E) clr[e.id] = idx;
return;
}
vector<edge> E0, E1;
for (edge e : E)
if (find(e.id).second) E1.push_back(e);
else E0.push_back(e);
solve(E0); solve(E1);
}
int main() {
memset(clr, -1, sizeof(clr));
scanf("%d%d%d", &nL, &nR, &m);
vector<edge> E(m);
for (int i = 0; i < m; i++) {
scanf("%d%d", &E[i].u, &E[i].v);
E[i].id = i;
E[i].v += nL;
}
solve(E);
printf("%d\n", idx);
for (int i = 0; i < m; i++)
printf("%d\n", clr[i]);
}