无向图的割顶和桥
- 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))
边-双联通分量
- 定义: 任意两点存在至少两条"边不重复"的路径 => 所有的边均不是桥
- 求法: 第一遍dfs找出所有的桥,第二遍dfs不经过桥,所经过的点均在同一双联通分量中
// 或者同求强联通分量的方法,low[u] == dfn[u] 即进行缩点(不加u)
void tarjan(int u,int fa){
dfn[u] = low[u] = ++stemp;
s.push(u); in[u] = true;
int son = 0,k = 1;
for(int i=head[u];i;i=e[i].nxt){
int v = e[i].to;
if(v==fa && k){k = 0; continue;} // 处理重边
if(!dfn[v]){
tarjan(v,u);
low[u] = min(low[u],low[v]);
}else if(in[v]){
low[u] = min(low[u],dfn[v]);
}
}
if(low[u] == dfn[u]){
int v; ++cnt_block;
do{
v = s.top(); s.pop();
belong[v] = cnt_block;
}while(v!=u);
}
}
点-双联通分量
- 定义: 任意两点至少存在两条"点不重复"的路径 => 分量内部无割点
- 求法: tarjan过程中遇到low[v] >= dfn[u] 将栈中所有点弹入一个联通分量(加入u但不将u弹出,因为u可能属于多个连通分量)
void tarjan(int u,int fa){
dfn[u] = low[u] = ++stemp;
s.push(u);
int son = 0,k = 1;
for(int i=head[u];i;i=e[i].nxt){
int v = e[i].to;
if(v==fa && k){k = 0; continue;} // 处理重边
if(!dfn[v]){
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u]){
if(fa != -1) cut[u] = 1;
blocks[++cnt_block].clear();
blocks[cnt_block].push_back(u); // 加入u
int now;
do{
now = s.top(); s.pop();
blocks[cnt_block].push_back(now);
}while(now != v); //栈弹到v,并加入双联通分量中
}
}else{
low[u] = min(low[u],dfn[v]);
}
}
if(fa==-1 && son>1) cut[u] = 1;
}