HDU 3836 Equivalent Sets(强连通分量,有向图改强连通图)

题目链接

题目大意

  给一个图问最少添加多少边之后它会变成强连通图。

解题思路

  对于一个缩点之后的dag图,我们考虑它的入度为0的点和出度为0的点。我们把一个入度为0的和出度为0的点相连,或者把一个入度为0的点连到另外一个度数大于等于2的点上,又或者把一个出度为0的点连到另外一个度数大于等于2的点上,都能构成一个新的连通分量,那么很明显,需要添加的最少的边就是入度为0的点与出度为0的点两者中的最大值。但是有一个特殊情况需要考虑,如果缩点之后只剩一个点,那么说明这个图本身就已经是一个强连通图了,那么就不需要添加边了。

代码

const int maxn = 2e5+10;
vector<int> e[maxn];
int n, m;
int dfn[maxn], low[maxn], dn;
int scc[maxn], sc, sk[maxn], tp; 
int in[maxn], out[maxn];
void tarjan(int u) {
    dfn[u] = low[u] = ++dn;
    sk[++tp] = u;
    for (auto v : e[u]) {
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if (!scc[v]) low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u]==low[u]) {
        ++sc;
        while(true) {
            int v = sk[tp--];
            scc[v] = sc;
            if (u==v) break; 
        }
    }
}
int main() {
    while(~scanf("%d%d", &n, &m)) {
        dn = sc = tp = 0;
        zero(dfn); zero(low); zero(scc); zero(in); zero(out); zero(sk);
        for (int i = 0; i<=n; ++i) e[i].clear();
        for (int i = 0, u, v; i<m; ++i) {
            scanf("%d%d", &u, &v);
            e[u].push_back(v);
        }
        int cnt = 0;
        for (int i = 1; i<=n; ++i)
            if (!dfn[i]) tarjan(i);
        for (int i = 1; i<=n; ++i) 
            for (auto v : e[i])
                if (scc[i]!=scc[v]) {
                    ++out[scc[i]];
                    ++in[scc[v]];
                }
        int maxi = 0, maxo = 0;
        for (int i = 1; i<=sc; ++i) {
            if (!in[i]) ++maxi;
            if (!out[i]) ++maxo;
        }
        if (sc==1) printf("0
");
        else printf("%d
", max(maxi,maxo));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/shuitiangong/p/12881655.html