Tarjan 算法
强连通分量
若图 \(G\) 中存在一个子图 \(G'\),\(G'\) 中任意两点均可互相到达,则称 \(G'\) 为 \(G\) 的一个强连通分量。若 \(G'\) 不存在一个严格子图为强连通分量,则 \(G'\) 为 \(G\) 的极大强连通分量。
图的强连通分量可以考虑使用 \(\rm Tarjan\) 算法求解。
\(\rm Tarjan\) 算法的思想是这样的,如果一个点的 \(\rm dfs\) 序等于他能访问到的最小的 \(\rm dfs\) 序,那么在 \(\rm dfs\) 树中,它的下方会出现一个强连通分量,且所有最小能访问到的 \(\rm dfs\) 序等于它的点,属于这个强连通分量。
时间复杂度 \(\Theta(n+m)\)。
参考代码:
void Tarjan (int u) {
dfn[u] = low[u] = ++tix, Sta[++top] = u;
for (int i = last[u]; i; i = Next[i])
if (!dfn[to[i]]) Tarjan(to[i]), low[u] = min(low[u], low[to[i]]);
else if (!SCC[to[i]]) low[u] = min(low[u], dfn[to[i]]);
if (dfn[u] == low[u]) {
SCC[u] = ++id;
while (Sta[top] != u) SCC[Sta[top--]] = id;
top--;
}
}
点/边双连通分量
如果在 \(\rm dfs\) 树上,某个结点 \(u\) 存在一个子节点 \(v\) ,满足 \(low_{v}\geqslant dfn_{u}\) ,则说明 \(v\) 想到达 \(u\) 的祖先结点必须经过 \(u\),所以 \(u\) 是一个割点。
当然,根节点不能通过这种方式来判断,应该看它的子节点数是否大于等于 \(2\),如果是,则它为割点。
遍历的时候要记得判掉父子边。
和判断割点的方法几乎一模一样,唯一的区别是判断 \(dfn_{u}\lt low_{v}\) 而不是 \(dfn_{u}\leqslant low_{v}\)。(如果从 \(v\) 出发连 \(u\) 都无法到达,那么 \((u, v)\) 就是一条割边。
判断割边的时候根节点不需要特殊考虑。
时间复杂度 \(\Theta(n+m)\)。
参考代码:
void Tarjan (int u, int rt) {
dfn[u] = low[u] = ++tix;
int son = 0;
for (int i = last[u]; i; i = Next[i]) {
if (!dfn[to[i]]) {
son++, Tarjan(to[i], rt);
low[u] = min(low[u], low[to[i]]);
if (low[to[i]] >= dfn[u] && u != rt) cut[u] = true;
}
low[u] = min(low[u], dfn[to[i]]);
}
if (u == rt && son > 1) cut[u] = true;
}
经典例题
[国家集训队] 稳定婚姻
首先在夫妻之间连有向边 \(B_{i}\to G_{i}\),然后在情人之间连有向边 \(G_{i}\to B_{i}\)。
可以发现,若 \(G_{i}\) 和 \(B_{i}\) 的婚姻情况发生了改变,那么一定存在一个从 \(G_{i}\) 开始 \(B_{i}\) 结束的环。
于是我们使用 \(\rm Tarjan\) 算法求强连通分量,若 \(B_{i}\) 与 \(G_{i}\) 属于同一个强连通分量,那他们一定属于一个环,则他们是不安全的,否则就是安全的。
时间复杂度 \(\Theta(n+m)\)。
[ZJOI2007] 最大半连通子图
首先缩点,发现强连通分量内的点一定属于同一个极大半连通子图。
然后将 \(\rm DAG\) 画出来发现最大半连通子图一定是某条从无入度结点到无出度结点的链,长度和方案数直接 \(\rm DAG\) 上 \(\rm DP\) 即可。
需要注意的是,在这题中,由于求的是导出子图,所以不能有重边。
时间复杂度 \(\Theta(n+m)\)。
[HNOI2012] 矿场搭建
先把个点和点双连通分量处理出来,对每个点双分开处理:
- 若该点双无割点,则需要建两个出口。
- 若该点双有一个割点,则需要在这个点双内建一个出口,且不能建在割点上。
- 若该点有两个以上割点,则不需要建出口,因为一定可以从某个割点跑出去。
方案数直接乘法原理计算即可。
时间复杂度 \(\Theta(n+m)\)。
[HAOI2010] 软件安装
可以发现这是一棵基环外向树,依题意环中结点必须选,剩下的构成一棵树,直接树上背包即可。
时间复杂度 \(\Theta(nm)\)。
