图的连通性【模板】
强连通分量【有向图】
强连通图:图G中任意两点之间都有可以到达的路径
强连通分量:有向图G的最大连通子图
最大连通子图:该子图是G的强连通子图,且将G中的任意一个不在该图中的点加入该子图中该子图将不再连通。
stack<int>s;
void Tarjan(int u){
Time++;
Dfn[u]=Time,Low[u]=Time;
s.push(u),Instack[u]=true;//标记点是否在栈中
for(int i=Last[u];i;i=Next[i]){
int v=End[i];
if(!Dfn[v]){
Tarjan(v);
Low[u]=min(Low[u],Low[v]);
}else if(Instack[v])//<u,v>是返祖边
Low[u]=min(Low[u],Dfn[v]);
if(Dfn[u]==Low[u]){//退栈 ,弹出整个强连通分量
int v;scc++;
do{
v=s.top(),s.pop();
Belong[v]=scc;//v属于scc号强连通分量
Instack[v]=false;
}while(u!=v);
}
}
}
割点【无向图】
无向连通图中的一个顶点u,若删去它以及其关联边,得到的新图至少包含两个连通分量。
条件:
①u是树根(搜索树),且至少有两棵子树
②u不是树根,且存在(u,v)使得Dfn[u]<=Low[v]
void Tarjan(int u,int fa){//fa是u的父亲
Time++;
Dfn[u]=Time,Low[u]=Time;
for(int i=Last[u];i;i=Next[i]){
int v=End[i];
if(!Dfn[v]){
Son[u]++;
Tarjan(v,u);
Son[u]+=Son[v];
//要排除u是根节点的情况,最后特判
if(Dfn[u]<=Low[v] && fa)cutP[u]=true;
}else if(v!=fa)//返祖边
Low[u]=min(Low[u],Dfn[v]);
}
if(!fa && Son[u]>1)cutP[u]=true;//u是树根,且至少有两个儿子
}
割边【无向图】
无向图中的一条边,删除它之后的新图将至少包含两个连通分量。
条件:
对于(u,v),有Dfn[u]
void Tarjan(int u,int fa){
Time++;
Low[u]=Time,Dfn[u]=Time;
for(int i=Last[u];i;i=Next[i]){
int v=End[i];
if(!Dfn[u]){
Tarjan(v,u);
Low[u]=min(Low[u],Low[v]);
if(Dfn[u]<Low[v])
Bridge[++cnt].x=u,Bridge[++cnt].y=v;
}else if(v!=fa)
Low[u]=min(Low[u],Dfn[v]);
}
}