Bridges & Articulation Points

O(n + m)

One lowlink DFS finds every bridge (edge whose removal disconnects the graph) and articulation point (vertex whose removal does). Track parent edges by id so parallel edges are handled correctly. Recursive: mind the stack limit on large graphs.

// use it on

CSES: Necessary Roads

The roads whose removal disconnects the network are the bridges, verbatim.

// the code

struct Cuts {
    int n, timer = 0;
    vector<vector<pair<int, int>>> adj;  // {to, edge id}
    vector<int> tin, low;
    vector<bool> isArt;
    vector<pair<int, int>> bridges;
    Cuts(int n) : n(n), adj(n), tin(n, -1), low(n), isArt(n, false) {}
    void add_edge(int id, int u, int v) {
        adj[u].push_back({v, id});
        adj[v].push_back({u, id});
    }
    void dfs(int u, int pe) {  // pe = id of the edge we arrived by
        tin[u] = low[u] = timer++;
        int children = 0;
        for (auto [v, id] : adj[u]) {
            if (id == pe) continue;
            if (tin[v] != -1) {
                low[u] = min(low[u], tin[v]);
            } else {
                dfs(v, id);
                low[u] = min(low[u], low[v]);
                if (low[v] > tin[u]) bridges.push_back({u, v});
                if (low[v] >= tin[u] && pe != -1) isArt[u] = true;
                children++;
            }
        }
        if (pe == -1 && children > 1) isArt[u] = true;  // root rule
    }
    void run() {
        for (int u = 0; u < n; u++)
            if (tin[u] == -1) dfs(u, -1);
    }
};

// more connectivity