Skip to content

概念

割点:给定无向联通图 G=(V,E),若删去节点 x 及其关联的边后,图 G 分裂成两个或两个以上不相连的子图,则称节点 x 为图 G 的割点。

割边:给定无向联通图 G=(V,E),若删去边 e 后,图 G 分裂成两个不相连的子图,则称边 e 为图 G 的割边或桥。

考虑特殊的图——树的割点与割边:

  1. 树中度大于 1 的节点都是割点
  2. 树中任意一条边都是割边

如果树加上一条边之后就变成了环,而环上的边一定不是割边,环上的节点不一定不是割点。这就启发我们对图进行深度优先遍历,求出图的一颗搜索树,然后加边考虑环,根据搜索树求出图中的割点与割边。

割边

cpp
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10, M = 2e5 + 10;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], num;
bool bridge[M];
int n, m;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void tarjan(int x, int in_edge) {
    dfn[x] = low[x] = ++num;
    for (int i = h[x]; i != -1; i = ne[i]) {
        int y = e[i];
        if (!dfn[y]) {
            tarjan(y, i);
            low[x] = min(low[x], low[y]);
            if (low[y] > dfn[x]) bridge[i] = bridge[i ^ 1] = true;
        } else if (i != (in_edge ^ 1)) {
            low[x] = min(low[x], dfn[y]);
        }
    }
}

int main() {
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i++) {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a);
    }

    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) tarjan(i, 0);
    }

    for (int i = 0; i < idx; i += 2) {
        if (bridge[i]) cout << e[i ^ 1] << ' ' << e[i] << endl;
    }

    return 0;
}

割点

cpp
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10, M = 2e5 + 10;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], num;
bool cut[N];
int n, m, root;

void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void tarjan(int x) {
    dfn[x] = low[x] = ++num;
    int child = 0;
    for (int i = h[x]; i != -1; i = ne[i]) {
        int y = e[i];
        if (!dfn[y]) {
            tarjan(y);
            low[x] = min(low[x], low[y]);
            if (low[y] >= dfn[x]) {
                child++;
                if (x != root || child > 1) cut[x] = true;
            }
        } else low[x] = min(low[x], dfn[y]);
    }
}

int main() {
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i++) {
        int a, b; cin >> a >> b;
        add(a, b), add(b, a);
    }

    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) root = i, tarjan(i);
    }
    
    for (int i = 1; i <= n; i++) {
        if (cut[i]) cout << i << " ";
    }

    return 0;
}