【题解】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;
}
posted @ 2021-12-16 07:59  zghtyarecrenj  阅读(35)  评论(0编辑  收藏  举报