无向图的割顶和桥
- 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\))