双联通的tarjan算法
转自:https://www.zhihu.com/question/40746887/answer/88428236
连通分量有三种∶边双连通分量,点双连通分量,强连通分量,前两种属于无向图,后一种属于有向图
定义:
双连通分量又分双连通分量和边双连通分量两种。若一个无向图中的去掉任意一个节点(一条边)都不会改变此图的连通性,即不存在割点(桥),则称作点(边)双连通图。一个无向图中的每一个极大点(边)双连通子图称作此无向图的点(边)双连通分量。
代码如下:
点双联通 struct Edge{ int u,v; Edge(int _u,int _v):u(_u),v(_v){} }edge[maxn]; int dfn[maxn],low[maxn],cut[maxn],bccno[maxn]; vector<int>gra[maxn],bcc[maxn]; stack<int>stk; int cnt,bcnt; void tarjan(int f,int u){ dfn[u]=low[u]=++cnt; int child=0; int sz=gra[u].size(); for(int i=0;i<sz;i++){ int id=gra[u][i]; int v=edge[id].v; if(!dfn[v]){ stk.push(id); child++; tarjan(u,v); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]){ cut[u]=1; bcc[++bcnt].clear(); while(1){ int id=stk.top(); stk.pop(); int uu=edge[id].u; int vv=edge[id].v; if(bccno[uu]!=bcnt){ bcc[bcnt].push_back(uu); bccno[uu]=bcnt; } if(bccno[vv]!=bcnt){ bcc[bcnt].push_back(vv); bccno[vv]=bcnt; } if(uu==u&&vv==v){ break; } } } }else if(dfn[v]<dfn[u]&&v!=f){ stk.push(id); low[u]=min(low[u],dfn[v]); } } if(f<0&&child==1){ cut[u]=0; } } 边双联通 struct Edge{ int u,v; Edge(int _u,int _v):u(_u),v(_v){} }edge[maxn]; int dfn[maxn],low[maxn],bccno[maxn]; vector<int>gra[maxn],bcc[maxn]; bool isb[maxn]; void tarjan(int f,int u){ dfn[u]=low[u]=++cnt; int sz=gra[u].size(); for(int i=0;i<sz;i++){ int id=gra[u][i]; int v=edge[id].v; if(!dfn[v]){ tarjan(u,v); low[u]=min(low[u],low[v]); if(low[v]>dfn[u]){ isb[id]=isb[id^1]=1; } }else if(dfn[v]<dfn[u]&&v!=f){ low[u]=min(low[u],dfn[v]); } } } void dfs(int u){ dfn[u]=1; bccno[u]=bcnt; int sz=gra[u].size(); for(int i=0;i<sz;i++){ int id=gra[u][i]; int v=edge[id].v; if(isb[id]){ continue; } if(!dfn[v]){ dfs(v); } } } int main(){ for(int i=1;i<=n;i++){ if(!dfn[i]){ tarjan(-1,i); } } memset(dfn,0,sizeof(dfn)); for(int i=1;i<=n;i++){ if(!dfn[i]){ bcnt++; dfs(i); } } }