题目链接
如果知道 X X X 可能有一些网络流的做法,但是在此题中网络流也显得不够高效。设最大度数为 D D D,可以构造找到一个 X = 2 ⌈ log 2 D ⌉ X=2^{\lceil \log_2 D \rceil} X=2⌈log2D⌉ 的解:
假如 D = 1 D=1 D=1,全都是一种颜色。
否则我们可以考虑将所有的边划分成两个集合,每个边集都最多 ⌈ D 2 ⌉ \left\lceil \frac D2 \right\rceil ⌈2D⌉ 条边连接一个节点:
有一个 bonus,可以证明 C C C 为最大度数。
- C C C 不可能小于最大度数,这一点显然。
- 对度数进行归纳,假设当前二分图 G = ( L , R , E ) G=(L,R,E) G=(L,R,E) 最大度数为 C C C,下述 (3.) 将证明可以给出一个不相交边集 S ⊆ E S\subseteq E S⊆E,使得 E \ S E\backslash S E\S 的最大度数为 C − 1 C - 1 C−1。如能够给出,则归纳说明找到了一个 C C C 种颜色的解。
- 考虑将 L L L 中节点删去所有度数不为 C C C 的点以及其出边后得到 L ∗ L^* L∗,4. 中将证明我们一定能找到一组 L ∗ L^* L∗ 的完美匹配。同理得到一组 R ∗ R^* R∗ 的完美匹配,考虑两个边集的并,考虑将匹配看成一条有向边,每个点至多有一条出边,我们只需要保证删去一些边后每个原本有出边的点都仍然有出边或入边,因此如果有环则是偶环,隔一个删一个,然后将剩余的链的头部保留下一个删除,下一个保留……最后得到的显然是满足 2. 要求的一个边集。
- 考虑 L ∗ L^* L∗ 的任一子集 S S S 以及其出边覆盖的对应点集合 T T T,记 d ( u ) d(u) d(u) 是原图中的度数, d ′ ( u ) d'(u) d′(u) 是只考虑跨越 S , T S,T S,T 间边集上的边的情况下度数,可以得知 C ∣ S ∣ = ∑ u ∈ S d ( v ) = ∑ v ∈ T d ′ ( v ) ≤ ∑ v ∈ T d ( v ) ≤ C ∣ T ∣ C|S|=\sum_{u\in S} d(v)=\sum_{v\in T} d'(v) \le \sum_{v\in T}d(v)\le C|T| C∣S∣=∑u∈Sd(v)=∑v∈Td′(v)≤∑v∈Td(v)≤C∣T∣,因此 ∣ S ∣ ≤ ∣ T ∣ |S|\le |T| ∣S∣≤∣T∣,由 Hall 定理,必然存在一组完美匹配。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
struct E {
int v, i;
E* next;
};
const int M = 500010, N = 200010;
int a, b, m, c;
int u[M], v[M], id[M], ans[M];
int deg[N], stk[N], si[N];
bool curc[N];
tuple<int, int, int> dat[M];
bool vis[N], evs[M], col[M];
E pool[M * 2 + N];
E *g[N], *t[N], *pt;
void adde(E** gg, int u, int v, int i) {
E* p = pt++;
p->v = v;
p->i = i;
p->next = gg[u];
gg[u] = p;
}
void dis(int uu) {
if (vis[uu]) return;
int s = 0;
++s; stk[s] = uu; vis[uu] = true; si[s] = -1;
while (s) {
int u = stk[s];
if (si[s] != -1) {
adde(t, u, g[u]->v, g[u]->i);
++deg[u];
g[u] = g[u]->next;
}
si[s] = 1;
while (g[u] && evs[g[u]->i]) g[u] = g[u]->next;
if (!g[u]) {
--s;
continue;
}
int v = g[u]->v, i = g[u]->i;
evs[i] = true;
if (vis[v]) {
bool f = false;
while (stk[s] != v) {
vis[stk[s]] = false;
col[g[stk[s]]->i] = f;
g[stk[s]] = g[stk[s]]->next;
f = !f;
--s;
}
col[g[v]->i] = f;
g[v] = g[v]->next;
si[s] = -1;
} else {
++s;
stk[s] = v;
si[s] = -1;
vis[v] = true;
}
}
int ql = 0, qr = 0;
stk[++qr] = uu;
while (ql < qr) {
int u = stk[++ql];
E* p = t[u];
int a = deg[u] / 2;
bool f = curc[u];
while (a--) {
col[p->i] = f;
curc[p->v] = f;
stk[++qr] = p->v;
p = p->next;
}
f = !f;
while (p) {
col[p->i] = f;
curc[p->v] = f;
stk[++qr] = p->v;
p = p->next;
}
}
}
void solve(int l, int r) {
bool f = false;
pt = pool;
for (int i = l; i <= r; ++i) {
++deg[u[i]]; ++deg[v[i]];
f |= deg[u[i]] > 1 || deg[v[i]] > 1;
}
if (!f) {
++c;
for (int i = l; i <= r; ++i) {
deg[u[i]] = deg[v[i]] = 0;
ans[id[i]] = c;
}
return;
}
for (int i = l; i <= r; ++i) {
adde(g, u[i], v[i], i);
adde(g, v[i], u[i], i);
deg[u[i]] = deg[v[i]] = 0;
}
for (int i = l; i <= r; ++i) {
dis(u[i]);
dis(v[i]);
}
int cnt = count(evs + l, evs + r + 1, true);
for (int i = l; i <= r; ++i) {
deg[u[i]] = deg[v[i]] = 0;
vis[u[i]] = vis[v[i]] = evs[i] = false;
g[u[i]] = g[v[i]] = t[u[i]] = t[v[i]] = NULL;
}
int il = l, ir = r;
for (int i = l; i <= r; ++i)
dat[col[i] ? ir-- : il++] = make_tuple(id[i], u[i], v[i]);
for (int i = l; i <= r; ++i)
tie(id[i], u[i], v[i]) = dat[i];
solve(l, il - 1);
solve(ir + 1, r);
}
int main() {
pt = pool;
scanf("%d%d%d", &a, &b, &m);
for (int i = 1; i <= m; ++i) {
scanf("%d%d", &u[i], &v[i]);
v[i] += a;
}
iota(id + 1, id + m + 1, 1);
solve(1, m);
printf("%d\n", c);
for (int i = 1; i <= m; ++i)
printf("%d\n", ans[i]);
return 0;
}