【图的连通】Tarjan
千古神犇Tarjan,扑通扑通跪下来 sto sto Tarjan orz orz
强联通分量: 思路其实很简单就是dfs跑跑跑,看能不能返祖为一个圈。具体实现并不需要top排序(只是top排序之后可以保证最少次数的从根节点(搜索树根)开始跑tarjan)
void tarjan(int x) { dfn[x]=low[x]=++dfsx; sta[++top]=x; inst[x]=1; int ss=ve[x].size(); for(int i=0;i<ss;i++) { if(!dfn[ve[x][i]]) { tarjan(ve[x][i]); low[x] = min(low[x],low[ve[x][i]]); } else if(inst[ve[x][i]]) low[x] = min(low[x],dfn[ve[x][i]]); } int to; if(low[x]==dfn[x]) { ++scc; do{ to = sta[top]; inst[to]=0; blo[to]=scc; top--; }while(to!=x); } }
以下针对Kosaraju不能解决的众多无向图问题
求割点的无向图tarjan:写法和tarjan差不多,但判low时边不能往父亲判,当一个结点u为割点的条件为对于自己的搜索子结点v,有low[v]>=dfn[u],或者作为根结点,在搜索树(!!!)中有两个或以上的子结点(即从这个点开始可以跑两次tarjan)。 求桥(割边)的无向图tarjan,和求割点差不多,当一条边为树当且仅当对于子结点连的边<u,v>有low[v]>dfn[u]。bool ispoint[];//割点 void tarjan(int x,int fu) { dfn[x] = low[x] = ++dfnx; fa[x] = fu; if(x==1)int child = 0; int ss=ve[x].size(); for(int i=0;i<ss;i++) { if(!dfn[ve[x][i]]) { if(x==1) child++; tarjan(ve[x][i],x); low[x]=min(low[x],low[ve[x][i]]); if(low[ve[x][i]]<=dfn[x]&&x!=1) ispoint[ve[x][i]]=1; if(low[ve[x][i]]>dfn[x]) { printf("from %d to %d is bridge",&x,&ve[x][i]);//桥 } } else if(ve[x][i]!=fu) { low[x] = min(low[x],dfn[ve[x][i]]); } } if(x!=1) return; if(child>=2) { ispoint[x]=1; } }一些概念: 点双连通分量:不含割点的极大连通子图 点双连通分量很好找呀,在我们徐照每次找到一条边或反向边就把这条边加入栈,当发现割点<u,v>边,我们就一个个出栈,直到边<u,v>,取出来这些点就是一些点双。 割点可以属于多个点双连通分量,其他点和每条边只属于一个点双。
bool ispoint[];//割点 void tarjan(int x,int fu) { stack<int>s; dfn[x] = low[x] = ++dfnx; fa[x] = fu; if(x==1)int child = 0; for(int it=la[x];it;it=nt[it]) { if(!dfn[en[it]]) { s.push(it); if(x==1) child++; tarjan(en[it],x); low[x]=min(low[x],low[en[it]]); if(low[en[it]]<=dfn[x]) { ispoint[x]=1; bcc++; while(233) { int k=s.top(); s.pop(); int x=st[k]; int y=en[k]; if(bl[x]!=bcc) { blo[bcc].push_back(x); bl[x]=bcc; } if(bl[y]!=bcc) { blo[bcc].push_back(y); bl[y]=bcc; } if(k==it) break; } } } else if(en[it]!=fu) { s.push(it); low[x] = min(low[x],dfn[ve[x][i]]); } } if(x!=1) return; if(child<2) { ispoint[x]=0; } }边双连通分量:不含割边的极大连通子图 将所有的边求出了后,直接将所有的割边全部删除,然后原本的图就分成了若干个连通的块块,那么对于每个块就是一个边双。 桥不属于任何边双,对于其他的点和边只属于一个边双。 代码这个就不给了owo。