Tarjan
定义
- 强连通分量:
对于一个有向图,如果图中任意两个结点均连通,那么我们称这个图是强连通的。
对于一个有向图中的极大强连通子图,我们称这个子图为强连通分量。
- 割点:
对于一个无向连通图中的点,假如删去这个点以及与其相连的所有边之后图不连通,那么称该点为该图的割点。
- 割边:
对于一个无向连通图中的边,假如删去这条边之后图不连通,那么称该边为该图的割边。
- 点双连通分量:
对于一个无向图,如果该图不包含割点,那么称我们这个图是点双连通的。
对于一个无向图中的极大点双连通子图,我们称这个子图为点双连通分量。
- 边双连通分量:
对于一个无向图,如果该图不包含割边,那么我们称这个图是边双联通的。
对于一个无向图中的极大边双连通子图,我们称这个子图为边双连通分量。
强连通分量
void tarjan(int s){
dfn[s] = low[s] = ++ cnt;
in[s] = 1; sk[++ tt] = s;
for (auto v : to[s]) {
if (!dfn[v]) {
tarjan(v);
low[s] = min(low[s], low[v]);
}
else if(in[v]) low[s] = min(low[s], dfn[v]);
}
if (dfn[s] == low[s]) {
num ++;
for (; ; ) {
int t = sk[tt --];
rid[t] = num; in[t] = 0;
scc[num].push_back(t);
if (t == s) break;
}
}
}
点双连通分量
void tarjan(int s, int fr){
dfn[s] = low[s] = ++ cnt;
sk[++ tt] = s;
bool child = 0;
for (auto v : to[s]) {
if (!dfn[v]) {
child = 1;
tarjan(v, s);
low[s] = min(low[s], low[v]);
if (low[v] >= dfn[s]) {
num ++;
while (sk[tt + 1] != v)
vDcc[num].push_back(sk[tt --]);
vDcc[num].push_back(s);
}
}
else low[s] = min(low[s], dfn[v]);
}
if (!fr && !child) vDcc[++ num].push_back(s);
}
边双连通分量
void tarjan(int s, int fr) {
low[s] = dfn[s] = ++ cnt;
sk[++ tt] = s; in[s] = 1;
for (int i = head[s]; i; i = nxt[i]) {
int v = to[i];
if (!dfn[v]) {
tarjan(v, i);
low[s] = min(low[s], low[v]);
if (low[v] > dfn[s]) bridge[i] = bridge[i ^ 1] = 1;
}
else if (i != (fr ^ 1)) low[s] = min(low[s], dfn[v]);
}
}
void dfs(int s){
Dcc[num].push_back(s); vis[s] = 1;
for (int i = head[s]; i; i = nxt[i]) {
int v = to[i];
if (vis[v] || bridge[i]) continue;
dfs(v);
}
}