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\n");
else printf("%d\n", max(maxi,maxo));
}
return 0;
}