1 条题解

  • 0
    @ 2026-10-26 19:23:52

    思路与算法

    用迭代lowlink和边栈分解点双连通块。单边块中的边不在任何环;非平凡块若边数等于顶点数,则是简单环,每边恰在一个环;边数更多的块中每边至少在两个环。按块累计题目要求的两类边。

    正确性说明

    每个简单环完整属于唯一点双块。二连通块中各点度至少2,边数等于顶点数强制全为2,即简单环。若不是简单环,由耳分解可知每条边所在环都有额外耳或等价替代路径,从而可构成第二个不同简单环。因此块分类对应题意。

    复杂度

    每组读入、栈与数组初始化、DFS及各块边处理均O(N+M),空间O(N+M);多组遇0 0结束。

    易错点

    原题无重边无自环,不将此结论直接套用到其他协议。孤点不产生边块。深链用显式栈,避免递归爆栈;块顶点用时间戳去重。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        while (cin >> n >> m && n) {
            vector<vector<pair<int, int>>> g(n);
            vector<pair<int, int>> edge(m);
            for (int i = 0; i < m; i++) {
                int a, b;
                cin >> a >> b;
                edge[i] = {a, b};
                g[a].push_back({b, i});
                g[b].push_back({a, i});
            }
            vector<int> dfn(n), low(n), par(n, -1), pe(n, -1), cur(n), stackE, stamp(n);
            int timer = 0, mark = 0, bridges = 0, multi = 0;
            for (int root = 0; root < n; root++)
                if (!dfn[root]) {
                    vector<int> st(1, root);
                    dfn[root] = low[root] = ++timer;
                    while (!st.empty()) {
                        int u = st.back();
                        if (cur[u] < (int)g[u].size()) {
                            auto e = g[u][cur[u]++];
                            if (e.second == pe[u])
                                continue;
                            int v = e.first;
                            if (!dfn[v]) {
                                par[v] = u;
                                pe[v] = e.second;
                                stackE.push_back(e.second);
                                dfn[v] = low[v] = ++timer;
                                st.push_back(v);
                            } else if (dfn[v] < dfn[u]) {
                                low[u] = min(low[u], dfn[v]);
                                stackE.push_back(e.second);
                            }
                        } else {
                            st.pop_back();
                            if (par[u] >= 0) {
                                int p = par[u];
                                low[p] = min(low[p], low[u]);
                                if (low[u] >= dfn[p]) {
                                    int E = 0, V = 0, id;
                                    ++mark;
                                    do {
                                        id = stackE.back();
                                        stackE.pop_back();
                                        E++;
                                        for (int v : {edge[id].first, edge[id].second})
                                            if (stamp[v] != mark) {
                                                stamp[v] = mark;
                                                V++;
                                            }
                                    } while (id != pe[u]);
                                    if (E == 1)
                                        bridges++;
                                    else if (E > V)
                                        multi += E;
                                }
                            }
                        }
                    }
                }
            cout << bridges << ' ' << multi << '\n';
        }
    }
    
    • 1

    信息

    ID
    1835
    时间
    1000ms
    内存
    32MiB
    难度
    普及+
    标签
    递交数
    0
    已通过
    0
    上传者