无向图的割顶和桥
- low[i] 表示 i号节点能访问到最早节点的编号
- dfn[i] 表示 i号节点在dfs序中的编号
void tarjan(int u,int fa){
int v;
int child = 0,k = 0;
dfn[u] = low[u] = ++dfs_clock;
for(int i=head[u];i!=-1;i=edge[i].nxt){
v = edge[i].to;
if(v == fa && !k){ // 处理重边 第一次访问到父亲则不访问
k++;
continue;
}
if(!dfn[v]){ // v是u的儿子
child++;
tarjan(v,u);
low[u] = min(low[u],low[v]); // 儿子能访问到,父亲也能访问到
if(low[v] > dfn[u]){ // 儿子不能访问到父亲前面的点
// is_cut[i] = true; // i号边为割边
}
if(low[v] >= dfn[u] && fa!=-1){ // 非树根且儿子不能访问他前面的点
iscutpoint[u] = true;
}
}else{ // v已经被访问过, 即u可以绕过父亲来访问更靠前的点
low[u] = min(low[u],dfn[v]);
}
}
if(fa == -1 && child>1 ){ // 有超过两个儿子的树根一定是割点
iscutpoint[u] = true;
}
}
非根
如图 2的儿子连回了他的父亲1的父亲,导致1不能成为割点. 而若是2的儿子连回了1,则1仍然可以作为割点
对于桥,若2的儿子既不能连回1也不能连回1的父亲,即图中蓝色的边不存在,则1-2这条边即为割边
树根
不同子树之间只能通过树根相连,所以只要超过两颗子树树根就是割点
对于根的子树互相相连,可以认为只有一颗子树,其余与根相连的边均为反向边即可.
- 割边:\(low[v] > dfn[u]\)
- 割点:\(low[v] >= dfn[u]\) || (\(is_root\) && \(son_cnt>1\))