CF1916F Group Division记录

发布时间 2024-01-01 23:30:39作者: cccpchenpi

题目链接:https://codeforces.com/contest/1916/problem/F

题意

已知点双连通的图 \(G = (V, E)\)。求一种划分方式 \(V = V_1 \cup V_2\),使得 \(|V_1| = n_1\)\(|V_2| = n_2\),且 \(G\) 的两生成子图 \(G[V_1]\)\(G[V_2]\) 均连通。

\(n_1, n_2 \le 2000\)\(m \le 5000\)

题解(官解)

首先给出构造方式:初始化 \(|V_1|\) 为任意只包含一个点的集合,之后向其中一次加入一个满足下面条件的 \(|V_2|\) 中的点 \(v\)

  • \(v\) 在原图中与 \(G[V_1]\)连通;

  • \(v\) 不是 \(G[V_2]\) 的割点。

显然这样的构造方式一定符合要求。下面证明这样的点一定存在:

  • \(G[V_2]\) 中没有割点,则显然一定存在。

  • 否则,选取 \(G[V_2]\) 的一个割点 \(u\),使得 \(G[V_2 - u]\) 至少有一个连通片 \(W\) 中没有 \(G[V_2]\) 的割点(这样的点一定存在,可以从 Tarjan 算法的 dfs 树考虑)。则 \(W\) 中必然有点与 \(V_1\) 相邻,否则删去 \(u\)\(W\)\(V_1\) 在原图中不连通,与原图点双连通的条件矛盾。取这样的点作为答案即可。

时间复杂度 \(O(n_1(n_1 + n_2 + m))\)

代码实现

void solve() {
    int n1, n2, m;
    cin >> n1 >> n2 >> m;
    int n = n1 + n2;
    vector<pair<int, int>> es(m);
    vector<vector<int>> oadj(n);
    for (int ei = 0; ei < m; ei++) {
        int u, v;
        cin >> u >> v;
        u--, v--;
        oadj[u].push_back(v);
        oadj[v].push_back(u);
        es[ei] = {u, v};
    }
    vector<bool> choose(n, false);
    choose[0] = true;
    for (int _ = 1; _ < n1; _++) {
        int nxt = -1;
        vector<vector<int>> adj(n);
        for (auto [u, v] : es) {
            if (!choose[u] && !choose[v]) {
                adj[u].push_back(v);
                adj[v].push_back(u);
            }
        }
        auto cut = cut_vertices(adj);
        for (int i = 0; i < n; i++) {
            if (choose[i]) {
                for (int j : oadj[i]) {
                    if (!choose[j] && !cut[j]) nxt = j;
                }
            }
        }
        assert(nxt != -1);
        choose[nxt] = true;
    }
    for (int i = 0; i < n; i++)
        if (choose[i]) cout << i + 1 << " ";
    cout << "\n";
    for (int i = 0; i < n; i++)
        if (!choose[i]) cout << i + 1 << " ";
    cout << "\n";
}