算法笔记——强联通分量&缩点

强联通分量(Strongly Connected Components):有向图的极大强连通子图。

为了搞清楚SCC是怎么回事,我们得先明白有向图中的 44 种边。

  • A. 树枝边:DFS树上的边,即指向未访问过节点的边
  • B. 前向边:指向DFS树中子树中节点的边
  • C. 后向边:指向DFS树中父亲的边
  • D. 横叉边:其他边,即指向DFS树中非子树的边

在这里插入图片描述
我们需要维护 22 个东西

  • 一个是 dfndfn 就是 dfsdfs 序,也是我们常说的时间戳。
  • 一个是 lowlow 就是这个点所能到达子树中的,深度最小的点祖先的dfs序编号。

dfndfn 处理起来很简单,直接 dfsdfs 的时候编一下号就行了。

lowlow 的初始值为 dfndfn

对于边(u,v)

  • 若为树枝边,则用low[v]更新
  • 若为前向边,因为指向的点的信息已经通过树枝边传递过来,所以无需更新
  • 若为后向边,则用dfn[v]更新
  • 若为横叉边,则指向另一个强连通分量,无需更新

代码:

int s[MAXN], stop;
int dfn[MAXN], low[MAXN];
int scccnt, sccnum[MAXN];
int dfscnt;

inline void tarjan(int now){
    dfn[now] = low[now] = ++dfscnt;
    s[stop++] = now;
    for (int i = he[now]; i != 0 ; i = ne[i]){
        if (!dfn[ed[i]]) {
            tarjan(ed[i]);
            low[now] = min(low[now], low[ed[i]]);
        } else if(!sccnum[ed[i]]) {
            low[now] = min(low[now], dfn[ed[i]]);
        }
    }

    if (dfn[now] == low[now]) {
        scccnt++;
        do {
            sccnum[s[--stop]] = scccnt;
        } while(s[stop] != now);
    }
}

sccnum这里是维护了一个栈,判断横插边。

例题:

原文地址:https://www.cnblogs.com/zhaohaikun/p/12816967.html