(模板)强连通分量
强连通分量(Tarjan)
个人认为:求强连通分量的过程就是缩点的过程
算法主要步骤
- \(dfn(n)\)时间戳
- \(low(n)\)点\(n\)和它子树中的返祖边和横插边能连到还没有出栈的\(dfn\)的最小的点
- 在\(dfs\)的时候维护一个栈,第一次访问某一个节点时就把这个点加入到栈中,当一个点\(x\)的\(dfn(x) = low(x)\)时,它就是这个强连通分量在搜索树中最高的点,将栈里的点出栈,知道点\(x\)也出栈,这些点构成一个强连通分量
一些思考
- 当\(dfn(x) = low(x)\)时,它就不能通过返祖边或者横插边去到比它更靠上的点了
- 理解它的正确性:
- 在dfs中,先出栈的点一定能被后出栈的点到达
- 越是靠后出栈就越有可能到达不了 (\(Kasaraju\))
- 所以当访问到了一个\(low(x) = dfn(x)\)的点时,一定是通过横插边或者返祖边回到了之前某个点,也就组成了一个强连通分量
代码
const int maxn = 1e5;
int dfn[maxn], low[maxn];
int col[maxn]; // 每个点属于哪一个强连通分量
bool vis[maxn]; // 是否在我维护的栈中
int cnt = 0;
int num = 0; // 有几个强连通分量
std::stack<int> stk;
std::vector<int> edge[maxn];
void tarjan(int x)
{
dfn[x] = low[x] = ++cnt;
vis[x] = 1;
stk.push(x);
for (int i = 0; i < edge[i].size(); i++)
{
int y = edge[x][i];
if (dfn[y] == 0) // 树边
{
tarjan(y);
low[x] = std::min(low[x], low[y]);
}
else if (vis[y] == 1) // 在栈中才有效 返祖边或横插边
{
low[x] = std::min(low[x], dfn[y]);
}
}
if (low[x] == dfn[x])
{
num++;
while(true)
{
int top = stk.top();
col[top] = num;
vis[top] = 0;
stk.pop();
if (x == top) break;
}
}
}

浙公网安备 33010602011771号