HDU ~ 4612 ~ Warm up(边双联通缩点 + 树的直径)

在这里插入图片描述

题意

N个点M条边的图,问如果加一条边, 最少可以剩下多少个桥?

思路

边双联通缩点以后形成一棵的树,所有树边均为桥。
环上的边显然不是桥,所以我们使得最长的一条链称为环,也就是直径。
那么答案就是:原来的桥数 - 直径
坑点是:有重边!!!重边自然不算是桥了。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXM = 2e6 + 5;
//边双连通分量
struct Edge
{
    int from, to;
    Edge(int u, int v) : from(u), to(v) {}
};
struct EdgeBCC//边双连通分量
{
    int n, m;
    int DFN[MAXN], LOW[MAXN], bccno[MAXN], dfs_clock, bcc_cnt;
    bool isbridge[MAXM];
    vector<Edge> edges;
    vector<int> G[MAXN];
    stack<int> S;

    void init(int n)
    {
        this->n = n, m = 0;
        edges.clear();
        for (int i = 0; i <= n; i++) G[i].clear();
    }

    void AddEdge (int from, int to)
    {
        edges.emplace_back(from, to);
        edges.emplace_back(to, from);
        m = edges.size();
        G[from].push_back(m - 2);
        G[to].push_back(m - 1);
    }

    void dfs(int u, int fa)
    {
        DFN[u] = LOW[u] = ++dfs_clock;
        S.push(u);
        for (auto id : G[u])
        {
            if ((id^1) == fa) continue;
            int v = edges[id].to;
            if (!DFN[v])
            {
                dfs(v, id);
                LOW[u] = min(LOW[u], LOW[v]);
                if (LOW[v] > DFN[u])//桥
                    isbridge[id] = isbridge[id^1] = true;
            }
            else if (!bccno[v] && LOW[u] > DFN[v])
                LOW[u] = min(LOW[u], DFN[v]);
        }
        if (LOW[u] == DFN[u])
        {
            bcc_cnt++;
            while (1)
            {
                int x = S.top(); S.pop();
                bccno[x] = bcc_cnt;
                if (x == u) break;
            }
        }
    }

    void find_bcc()
    { // 注意点的编号从0开始, 连通分量的编号从1开始
        dfs_clock = bcc_cnt = 0;
        memset(DFN, 0, sizeof(DFN)), memset(bccno, 0, sizeof(bccno));
        memset(isbridge, 0, sizeof(isbridge));
        for (int i = 0; i < n; i++)
            if (!DFN[i]) dfs(i, -1);
    }

}gao;

int d[MAXN], s;
vector<int> G[MAXN];
void dfs(int u, int fa)
{
    for (auto v: G[u])
    {
        if (v == fa) continue;
        d[v] = d[u] + 1;
        if (d[v] > d[s]) s = v;
        dfs(v, u);
    }
}
int main()
{
    int n, m;
    while (~scanf("%d%d", &n, &m) && (n + m))
    {
        gao.init(n);
        while (m--)
        {
            int u, v; scanf("%d%d", &u, &v);
            u--, v--;
            gao.AddEdge(u, v);
        }
        gao.find_bcc();
        for (int i = 0; i <= gao.bcc_cnt; i++) G[i].clear();
        for (int i = 0; i < gao.edges.size(); i += 2)
        {
            if (!gao.isbridge[i]) continue;
            Edge& e = gao.edges[i];
            int u = gao.bccno[e.from], v = gao.bccno[e.to];
            G[u].push_back(v);
            G[v].push_back(u);
        }
        s = 1;
        d[s] = 0; dfs(s, 0);
        d[s] = 0; dfs(s, 0);
        int ans = (gao.bcc_cnt - 1) - d[s];
        printf("%d\n", ans);
    }
    return 0;
}
/*
4 4
1 2 1 3 1 4 2 3 
6 6
1 2 2 3 3 4 4 5 2 6 6 2
7 8
1 2 2 3 3 4 4 5 2 6 6 2 1 3 4 7
5 4
2 1 2 3 2 4 2 5
5 5
2 1 2 3 2 4 2 5 3 4
5 5
1 2 2 3 3 4 3 4 4 5
0 0
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值