sol. CF1680F Lenient Vertex Cover

发布时间 2023-09-10 23:21:35作者: WTR2007

CF1680F Lenient Vertex Cover

下面用 \(G\) 表示一个环的边集,记作环 \(G\)

我们令一个环 \(G\) 的价值为它经过的返祖边数量,记作 \(Z(G)\),下面给出核心结论:

若存在一条边 \(e_0\) 经过所有 \(Z(G) = 1\) 的奇环,且不经过任意一个 \(Z(G) = 1\) 的偶环,那么 \(e_0\) 经过所有奇环,且不经过任意一个偶环


下面给出简单的证明:

定义两个环的异或值 \(G = G_1 \otimes G_2\) 满足:

\[G = \{ e \, \vert \, (e \in G_1 \land e \not\in G_2) \lor (e \in G_2 \land e \not\in G_1)\} \]

该运算显然满足交换律和结合律。

引理:对于任意环 \(G\),均可以被唯一分解为 \(G = G_1 \otimes G_2 \dots \otimes G_m\),其中 \(Z(G) = m\),满足 \(\forall 1 \le i \le m\),都有 \(Z(G_i) = 1\)

证明:考虑数学归纳法,当 \(m = 1\) 时显然成立。下面假定当 \(m = k\) 时成立,则当 \(m = k + 1\) 时,容易发现 \(G_{k + 1}\)\(G \otimes G_1 \otimes G_2 \dots \otimes G_k\) 显然相等,若不等,则通过 \(G_1, G_2, \dots, G_{k + 1}\) 中的返祖边显然不能成环,与环 \(G\) 矛盾。(具体的严谨证明笔者也不太会,只能感性理解)

若对于一个环 \(G = G_1 \otimes G_2 \dots \otimes G_m\),容易发现原环上的边由所有小环的边之和减去重合的边,即 \(|G| = \sum\limits_{i = 1}^{m} |G_i| - \Delta\), 且 \(2 \mid \Delta\),那么环 \(G\) 的奇偶性只和 \(G_1, G_2 \dots G_m\) 中的奇环数目 \(x\) 有关,具体的:

  • \(2 \mid x\),那么环 \(G\) 为偶环,若存在一条边 \(e_0\) 经过所有的奇环,那么 \(e_0\) 必然不经过 \(G\)

  • \(2 \nmid x\),那么环 \(G\) 为奇环,若存在一条边 \(e_0\) 经过所有的奇环,那么 \(e_0\) 必然经过 \(G\)

综上所述,原结论显然成立。


接下来的处理就很简单,只需要用树上差分统计每条边经过几个奇环和偶环,最后寻找是否存在一条 \(e_0 = (u_0, v_0)\) 满足经过所有奇环,且不经过一个偶环的边。

输出方案的话,若原图为二分图,直接染色即可,否则删掉 \(e_0\) 后,钦定 \(col_{u_0} = 1\) 染色即可,注意多测要清空。

时间复杂度为 \(\mathcal{O}(n + m)\)

代码
#include<bits/stdc++.h>
#define MULT_TEST 1
using namespace std;
inline int read() {
    int w = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        w = (w << 1) + (w << 3) + ch - 48;
        ch = getchar();
    }
    return w * f;
}
inline void Solve() {
    int n, m, sum = 0, t = 0, opt = -1;
    n = read(); m = read();
    vector<int> dep(n + 1), ans(n + 1), vis(n + 1), tag(n + 1), h(m + 1);
    vector<vector<pair<int, int>>> e(n + 1), E(n + 1);
    for (int i = 1; i <= m; i++) {
        int u = read(), v = read();
        e[u].push_back({v, i});
        e[v].push_back({u, i});
    }
    function<void(int, int)> DFS1 = [&](int x, int fa) {
        vis[x] = 1;
        dep[x] = dep[fa] + 1;
        for (auto [u, id] : e[x]) {
            if (u == fa) continue;
            if (vis[u]) {
                if (dep[u] > dep[x]) continue;
                if ((dep[x] - dep[u]) & 1) tag[u]++, tag[x]--, h[id]--;
                else sum++, tag[u]--, tag[x]++, h[id]++;
            }
            else {
                E[x].push_back({u, id});
                DFS1(u, x);
            }
        }
    };
    function<void(int, int)> DFS2 = [&](int x, int id2) {
        for (auto [u, id] : E[x]) {
            DFS2(u, id);
            tag[x] += tag[u];
        }
        if (id2 > 0) h[id2] = tag[x];
    };
    bool flag = 1;
    function<void(int, int)> DFS3 = [&](int x, int col) {
        vis[x] = 1;
        ans[x] = col;
        for (auto [u, id] : e[x]) {
            if (vis[u]) {
                if (ans[u] == col) flag = 0;
                continue;
            }
            if (id == opt) continue;
            DFS3(u, col ^ 1);
        }
    };
    DFS3(1, 1);
    if (flag) {
        printf("YES\n");
        for (int i = 1; i <= n; i++) printf("%d", ans[i]);
        puts("");
        return ;
    }
    vis.clear(); vis.resize(n + 1);
    DFS1(1, 1); DFS2(1, -1);
    for (int i = 1; i <= n; i++)
        for (auto [u, id] : e[i]) 
            if (h[id] == sum) opt = id, t = i;
    if (opt == -1) printf("NO\n");
    else {
        vis.clear(); vis.resize(n + 1);
        DFS3(1, 1);
        printf("YES\n");
        for (int i = 1; i <= n; i++) printf("%d", ans[i] ^ (ans[t] == 0));
        puts("");
    }
}
signed main() {
    int T = 1;
#if MULT_TEST
    T = read();
#endif 
    while (T--) Solve();
    return 0;
}