Tarjan算法杂谈
以后写无向图最好都记录一下来边from
,不要记录father
,因为前者更有通用性。
对于一个点双连通分量\(G\),除非该连通子图只有一个点,否则\(G\)至少有两个点。
证明:
只有一个点的情况,显然 \(|G|=1\)。
如果点数至少有两个:
随便取两个点,构成一个子图\(E\)。
根据定义,删去给图中任意一个点,都不会使图不连通,所以这是一个点双连通子图。由于点双连通分量是极大点双连通子图,所以其点数一定不少于2个。
边双连通分量中两点之间都至少包含在一个简单环中。
这个定理可以用来判简单环。
Tarjan的LCA用的不多。
有向图的强连通分量:
void tarjan(int u)
{
dfn[u] = low[u] = ++ timestamp;
stk[ ++ top] = u;
in_stk[u] = true;
for (int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (in_stk[v]) low[u] = min(low[u], low[v]);
}
if (dfn[u] == low[u])
{
++ scc_cnt;
int y;
do
{
y = stk[top -- ];
in_stk[y] = false;
id[y] = scc_cnt;
sz[scc_cnt] ++ ;
} while (y != u);
}
}
无向图的点双连通分量:
注意事项:坑点记录2022.11.10。
void tarjan(int u, int from)
{
dfn[u] = low[u] = ++ timestamp;
stk[ ++ top] = u;
int cnt = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if (!dfn[v])
{
cnt ++ ;
tarjan(v, i);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u])
{
if (root != u || cnt > 1) cut[u] = true;
++ vdcc_cnt;
int y;
do {
y = stk[top -- ];
vdcc[vdcc_cnt].push_back(y);
} while (y != v);
vdcc[vdcc_cnt].push_back(u);
}
}
else if (i != (from ^ 1))
low[u] = min(low[u], dfn[v]);
}
if (u == root && !cnt)
{
++ vdcc_cnt;
top -- ;
vdcc[vdcc_cnt].push_back(u);
}
}
无向图的边双连通分量
void tarjan(int u, int from)
{
dfn[u] = low[u] = ++ timestamp;
stk[ ++ top] = u;
for (int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if (!dfn[v])
{
tarjan(v, i);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u]) isbrg[i] = isbrg[i ^ 1] = true;
}
else if (i != (from ^ 1)) low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u])
{
++ dcc_cnt;
int y;
do {
y = stk[top -- ];
dcc[dcc_cnt].push_back(y);
} while (y != u);
}
}