首先先说一下概念
1.强连通:对于有向图中两点i,j,若存在i到j和j到i的路径,则称i,j强连通
2.弱连通:对于有向图中两点i,j,若存在i到j或j到i的路径,则称i,j弱连通
3.强连通图:任意两点均强连通的图
4.强连通分量:有向图的极大强连通子图
如何进行强连通分量缩点操作?
考虑维护两个数组,dfn和low,分别表示该点的dfs序和该点通过树边和至多一条连向当前强连通分量内部的非树边能访问到的dfn最小值。 如果一个点的能访问到最早的点为这个点本身(low值等于dfn),就会形成一个新的强连通分量。
很显然,因为形成了一个环,可以跳到它的祖先
如果v没有访问过,那么v是树边,low[u]=min(low[u],low[v])
如果v访问过,那么判断它是否已经属于一个scc,如果不属于,那么u和v一定是同一个scc,low[u]=min(low[u],dfn[v])
stack<int>s;
void dfs(int u){
dfn[u] = low[u]= ++dfncnt; //先把该点的dfs序和low都赋值成自己
s.push(u); //压入栈中
for(register int i(head[u]) ; i ; i=e[i].nxt){//链式前向星遍历
int v = e[i].to; //下一个点
if(!dfn[v]){ //如果该点没有被dfs过
dfs(v); //往下搜索
low[u] = min(low[u],low[v]); //把该点的low更新为它自己的和它儿子较小的那个
}
else if(!sccnum[v]) low[u] = min(low[u],dfn[v]); //如果该点被dfs到,而且不在一个强连通分量中,把该点的low更新
}
if(low[u] == dfn[u]){ //找到了一个强连通分量
scccnt++; //这是第几个强连通分量
while(1){
int x = s.top(); //一直往外弹元素
s.pop();
sccnum[x] = scccnt; //该点在第几个强连通分量中
sccsize[scccnt]++; //该强连通分量的size
if(x == u) break; //直到弹到它自己,它和它之前被弹出的元素在同一个强连通分量中
}
}
}