跳转至

割点和桥

割点

模版
C++
struct Graph {
    int n, rt, tot, ccnt;
    vector<vector<int>> adj;
    vector<int> dfn, low, cut, point;
    vector<bool> iscut;
    Graph(int n) : n(n), adj(n + 1), cut(n + 1), iscut(n + 1), dfn(n + 1), low(n + 1), tot(0), ccnt(0) { }
    void addEdge(int u, int v) {
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    void tarjan(int u) {
        dfn[u] = low[u] = ++tot;
        int child = 0;
        for (auto &to : adj[u]) {
            if (!dfn[to]) {
                child++;
                tarjan(to);
                low[u] = min(low[u], low[to]);
                if (low[to] >= dfn[u]) {
                    cut[u]++;
                    if (u != rt || child > 1) {
                        iscut[u] = true;
                    }
                }
            } else {
                low[u] = min(low[u], dfn[to]);
            }
        }
    }
    void cutPoint() {
        for (int i = 1 ; i <= n ; i++) {
            if (!dfn[i]) {
                rt = i;
                tarjan(i);
                ccnt++;
            }
        }
        for (int i = 1 ; i <= n ; i++) {
            if (iscut[i]) {
                point.push_back(i);
            }
        }
    }
}; // Graph
// 定义 Graph g(n) 表示一个 n 个点的无向图
// 使用 g.addEdge(u, v) 进行添加无向边 (u, v)
// 使用 g.cutPoint() 即可进行割点
// point 表示所有割点的编号
例题

P3388 【模板】割点(割顶) - 洛谷

习题

P5058 [ZJOI2004] 嗅探器 - 洛谷

\(n\) 个点的无向图, 求从 \(a\)\(b\) 上割点的最小编号。

\(a\) 为根, 求割点, 判断割点的dfn小于 \(b\) 的dfn即可

C++
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int __OneWan_2024 = [](){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    return 0;
}();
int a, b;
struct Graph {
    int n, rt, tot, ccnt;
    vector<vector<int>> adj;
    vector<int> dfn, low;
    vector<bool> iscut;
    Graph(int n) : n(n), adj(n + 1), iscut(n + 1), dfn(n + 1), low(n + 1), tot(0), ccnt(0) { }
    void addEdge(int u, int v) {
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    void tarjan(int u) {
        dfn[u] = low[u] = ++tot;
        int child = 0;
        for (auto &to : adj[u]) {
            if (!dfn[to]) {
                child++;
                tarjan(to);
                low[u] = min(low[u], low[to]);
                if (low[to] >= dfn[u]) {
                    if (u != rt && dfn[b] >= dfn[to]) {
                        iscut[u] = true;
                    }
                }
            } else {
                low[u] = min(low[u], dfn[to]);
            }
        }
    }
    void cutPoint() {
        rt = a;
        tarjan(a);
    }
}; // Graph
// 定义 Graph g(n) 表示一个 n 个点的无向图
// 使用 g.addEdge(u, v) 进行添加无向边 (u, v)
// 使用 g.cutPoint() 即可进行割点
int main() {
    int n;
    cin >> n;
    Graph g(n);
    int u, v;
    while (cin >> u >> v) {
        if (u == 0 && v == 0) break;
        g.addEdge(u, v);
    }
    cin >> a >> b;
    g.cutPoint();
    auto iscut = g.iscut;
    for (int i = 1 ; i <= n ; i++) {
        if (iscut[i]) {
            cout << i << "\n";
            return 0;
        }
    }
    cout << "No solution\n";
    return 0;
}

割边

模版
C++
struct Graph {
    int n, tot, ccnt;
    vector<vector<int>> adj;
    vector<int> dfn, low, bridge;
    vector<array<int, 2>> edges;
    Graph(int n) : n(n), adj(n + 1), dfn(n + 1), low(n + 1), tot(0), ccnt(0) { }
    void addEdge(int u, int v) {
        adj[u].push_back(edges.size());
        edges.push_back({u, v});
        adj[v].push_back(edges.size());
        edges.push_back({v, u});
    }
    void tarjan(int u, int e) {
        dfn[u] = low[u] = ++tot;
        for (auto idx : adj[u]) {
            int to = edges[idx][1];
            if (!dfn[to]) {
                tarjan(to, idx);
                low[u] = min(low[u], low[to]);
                if (low[to] > dfn[u]) {
                    bridge.push_back(idx);
                }
            } else if (dfn[to] < dfn[u] && idx != (e ^ 1)) {
                low[u] = min(low[u], dfn[to]);
            }
        }
    }
    void cutEdge() {
        for (int i = 1 ; i <= n ; i++) {
            if (!dfn[i]) {
                tarjan(i, -1);
                ccnt++;
            }
        }
    }
}; // Graph
// 定义 Graph g(n) 表示一个 n 个点的无向图.
// 使用 g.addEdge(u, v) 进行添加无向边 (u, v).
// 使用 g.cutEdge() 即可进行割边.
// bridge 表示所有桥边的编号.
C++
template<typename T>
struct Graph {
    struct Edge {
        int u, to;
        T val;
    };
    int n, tot, ccnt;
    vector<vector<int>> adj;
    vector<int> dfn, low, bridge;
    vector<Edge> edges;
    Graph(int n) : n(n), adj(n + 1), dfn(n + 1), low(n + 1), tot(0), ccnt(0) { }
    void addEdge(int u, int v, T val) {
        adj[u].push_back(edges.size());
        edges.push_back({u, v, val});
        adj[v].push_back(edges.size());
        edges.push_back({v, u, val});
    }
    void tarjan(int u, int e) {
        dfn[u] = low[u] = ++tot;
        for (auto idx : adj[u]) {
            int to = edges[idx].to;
            if (!dfn[to]) {
                tarjan(to, idx);
                low[u] = min(low[u], low[to]);
                if (low[to] > dfn[u]) {
                    bridge.push_back(idx);
                }
            } else if (dfn[to] < dfn[u] && idx != (e ^ 1)) {
                low[u] = min(low[u], dfn[to]);
            }
        }
    }
    void cutEdge() {
        for (int i = 1 ; i <= n ; i++) {
            if (!dfn[i]) {
                tarjan(i, -1);
                ccnt++;
            }
        }
    }
}; // Graph
// 定义 Graph<int> g(n) 表示一个 n 个点且权值在 int 范围内的无向图.
// 使用 g.addEdge(u, v, val) 进行添加无向边 (u, v) 权为 val.
// 使用 g.cutEdge() 即可进行割边.
// bridge 表示所有桥边的编号.
例题

Problem - 4738

\(n\) 个点 \(m\) 条边的无向带权图.

删掉一条边的花费为这条边的边权.

至多删掉一条边.

问能使得图不连通的最小花费(不能则为-1).

C++
int main() {
    int n, m;
    while (cin >> n >> m) {
        if (n == 0 && m == 0) break;
        Graph<int> g(n);
        for (int i = 0 ; i < m ; i++) {
            int u, v, w;
            cin >> u >> v >> w;
            g.addEdge(u, v, w);
        }
        g.cutEdge();
        if (g.ccnt == 1) {
            int ans = accumulate(begin(g.bridge), end(g.bridge), 0x7fffffff, [&](int res, int idx) {
                return min(res, g.edges[idx].val);
            });
            ans = max(ans, 1);
            if (ans == 0x7fffffff) ans = -1;
            cout << ans << "\n";
        } else {
            cout << 0 << "\n";
        }
    }
    return 0;
}