【算法竞赛进阶指南】(图论) Network 边双连通分量


题意分析:
我们可以利用双连通分量e-dcc将图缩点变成一棵树, 树上的边即为桥的数量。
接着我们分析加边的操作:

  1. 如果两个点在一个双连通分量中,那么加边并不会影响当前桥的数量
  2. 如果两个点不在一个双连通分量中,那么会影响桥的数量。可以转换为在树上加一条边,那么必然会形成一个环,接着只需要对环上的边进行标记,标记数为减少的桥的数量。找环的操作可以先找到两点的lca然后往上便利并标记即可。复杂度为O(M+Q*N),本题数据比较宽松,这样的作法可以直接过,当然也可以用并查集进行优化。
#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10;

int n, m, low[N], dfn[N], idx, bridge[N<<1], cnt, c[N], scc, h[N], cnt2, h2[N], vis[N<<1], C;

int d[N], f[N][30], t, q;
struct edge {
    int to, next;
}e[N << 1], e2[N << 1];


void add(int u, int v) {
    e[cnt].to = v;
    e[cnt].next = h[u];
    h[u] = cnt++;
}

void add_c(int u, int v) {
    e2[cnt2].to = v;
    e2[cnt2].next = h2[u];
    h2[u] = cnt2++;
}

void tarjan(int x, int in_edge) {
    low[x] = dfn[x] = ++idx;
    for (int i = h[x]; ~i; i = e[i].next) {
        int v = e[i].to;
        if (!dfn[v]) {
            tarjan(v, i);
            low[x] = min(low[x], low[v]);
            if (low[v] > dfn[x]) bridge[i] = bridge[i ^ 1] = 1;
        }
        else if (i != (in_edge ^ 1))
            low[x] = min(low[x], dfn[v]);
    }
}

void dfs(int x) {
    c[x] = scc;
    for (int i = h[x]; ~i; i = e[i].next) {
        int v = e[i].to;
        if (c[v] || bridge[i]) continue;
        dfs(v);
    }
}

void bfs(int x) {
    memset(d, 0, sizeof d);
    memset(f, 0, sizeof f);
    queue<int> q;
    q.push(x);
    d[x] = 1;
    while (q.size()) {
        int u = q.front();
        q.pop();
        for (int i = h2[u]; ~i; i = e2[i].next) {
            int v = e2[i].to;
            if (d[v]) continue;
            d[v] = d[u] + 1;
            f[v][0] = u;
            for (int j = 1; j <= t; j++) {
                f[v][j] = f[f[v][j - 1]][j - 1];
            }
            q.push(v);
        }
    }
}

int lca(int x, int y) {
    if (d[x] > d[y]) swap(x, y);
    for (int i = t; i >= 0; i--) if (d[f[y][i]] >= d[x]) y = f[y][i];

    if (x == y) return x;
    for (int i = t; i >= 0; i--) {
        if (f[y][i] != f[x][i])
            y = f[y][i], x = f[x][i];
    }
    return f[x][0];
}

int find(int x, int p) {
    int num = 0;
    while (x != p) {
        for (int i = h2[x]; ~i; i = e2[i].next) {
            if (e2[i].to == f[x][0] && !vis[i]) vis[i] = 1, num++;
        }
        x = f[x][0];
    }
    return num;
}

int main() {
    ios::sync_with_stdio(0);
    while (cin >> n >> m) {
        if (n == 0 && m == 0) break;
        printf("Case %d:\n", ++C);
        memset(h, -1, sizeof h);
        memset(h2, -1, sizeof h2);
        memset(bridge, 0, sizeof bridge);
        memset(c, 0, sizeof c);
        memset(dfn, 0, sizeof dfn);
        memset(low, 0, sizeof low);
        cnt = cnt2 = scc = idx = 0;
        for (int i = 0; i < m; i++) {
            int x, y;
            cin >> x >> y;
            add(x, y), add(y, x);
        }
        for (int i = 1; i <= n; i++) if (!dfn[i]) tarjan(i, 0);

        for (int i = 1; i <= n; i++) {
            if (!c[i]) {
                ++scc;
                dfs(i);
            }
        }

        int bridge_now = 0;
        for (int i = 0; i < cnt; i += 2) {
            int u = e[i].to, v = e[i ^ 1].to;
            if (c[u] == c[v]) continue;
            add_c(c[u], c[v]), add_c(c[v], c[u]);
            bridge_now++;
        }

        t = (int)(log(scc) / log(2)) + 1;
        bfs(1);
        
        memset(vis, 0, sizeof vis);
        cin >> q;
        for (int i = 0; i < q; i++) {
            int x, y;
            cin >> x >> y;
            if (c[x] == c[y]) {
                printf("%d\n", bridge_now);
                continue;
            }
            int p = lca(c[x], c[y]);
            bridge_now -= find(c[x], p);
            bridge_now -= find(c[y], p);
            printf("%d\n", bridge_now);
        }
        printf("\n");
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要获取无向图的双连通分量,可以使用Tarjan算法。以下是一个Python实现的示例代码: ``` def tarjan_biconnected_components(graph): """ Tarjan算法计算无向图的双连通分量 :param graph: 无向图,用邻接列表表示 :return: 双连通分量列表 """ index_counter = [0] stack = [] lowlink = {} index = {} result = [] bridges = [] def strongconnect(node): # 为节点赋予唯一的索引 index[node] = index_counter[0] lowlink[node] = index_counter[0] index_counter[0] += 1 stack.append(node) # 对于每个相邻节点v for v in graph[node]: # 如果v没有被访问过,则递归调用strongconnect if v not in index: strongconnect(v) lowlink[node] = min(lowlink[node], lowlink[v]) # 如果v是一个桥,则将桥添加到bridges列表中 if lowlink[v] == index[v]: bridges.append((node, v)) # 如果v已经在堆栈中,则更新此节点的lowlink elif v in stack: lowlink[node] = min(lowlink[node], index[v]) # 如果节点是一个连接分量的根,则弹出堆栈,并收集连通分量 if lowlink[node] == index[node]: connected_component = [] while True: v = stack.pop() connected_component.append(v) if v == node: break result.append(connected_component) for node in graph: if node not in index: strongconnect(node) return result ``` 使用示例: ``` graph = { 1: {2, 3}, 2: {1, 3, 4}, 3: {1, 2, 4}, 4: {2, 3, 5}, 5: {4} } result = tarjan_biconnected_components(graph) print(result) # 输出:[[1, 2, 3], [4, 5]] ``` 以上代码实现了Tarjan算法,用于计算无向图的双连通分量。传入的图以邻接列表表示,返回的结果是双连通分量的列表。对于结果中的每个双连通分量,其包含的节点组成了一个强连通分量,即任意两个节点都有一条路径相连。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值