2022.02.25 Tarjan
https://www.cnblogs.com/c1299401227/p/5402747.html
1. 求有向图强连通分量
1.1 为什么不是无向图
毕竟无向图中,一个边过去,还可以从这个边回来。
1.2 代码实现
inline void Tarjan(int x){
low[x]=dfn[x]=++ind;
stacki.push(x);instacki[x]=1;
for(int i=head[x];i;i=a[i].next){
int v=a[i].to;
if(!dfn[v])Tarjan(v),low[x]=min(low[x],low[v]);
else if(instacki[x])low[x]=min(low[x],low[v]);
}
int k=0;
if(low[x]==dfn[x]){
++tot;
do{
k=stacki.top();stacki.pop();
instacki[k]=0;
belong[k]=tot;
}while(k!=x);
}
}
2.求无向图割点
2.1 定义
割点:原来联通的图,删除某点后,图不连通。
2.2 思路
如果是根节点,那么它必须有两棵子树,否则,只有一棵子树,删了之后,还是联通的;
对于其他节点,它的子节点向上走,最多只能走到它自己,再向上就翻不上去。如果能翻到它上面,说明有环了,去掉它,,依旧联通,即\(low[v]>=dfn[x]\)。
2.3 代码实现
注意:因为是无向图,它不能再回到它爹那里去。
inline void Tarjan(int x,int fai){
dfn[x]=low[x]=++ind;
stakci.push(x);instacki[x]=1;
int son=0;
for(int i=head[x];i;i=a[i].next){
int v=a[i].to;
if(!dfn[v]){
Tarjan(v,x);
low[x]=min(low[x],low[v]);
if(low[v]>=dfn[x])cutstain[x]=1;
if(x==root)++son;
}else if(fai!=v)low[x]=min(low[x],dfn[v]);
}
if(x==root&&son==1)cutstain[x]=0;//x is root,but it only has one son.
}
3. 求无向图桥
3.1 定义
桥:原理来的图联通,删除一条边后,新图不连通。
3.2 思路
只有\(low[v]>>dfn[x]\)时,这一条从x到v的边才是桥。这是因为,如果是一个桥,它的儿子不能翻到它那里。
但是要注意:小心重边!!而且,从v到x可不是一个环,这只是一条无向边!
3.3 代码实现
inline void Trajan(int x,int fai){
dfn[x]=low[x]++ind;
stacki.push(x);instacki[x]=1;
for(int i=head[x];i;i=a[i].next){
int v=a[i].to;
if(!dfn[v]){
Tarjan(v,x);
low[x]=min(low[x],low[v]);
if(low[v]>dfn[x])cutedge[i]=1;
}else if(v!=fai)low[x]=min(low[x],dfn[v]);
}
}
4. 求无向图的边双
4.1 定义
如果一个无向连通图中,没有割边,那么这个无向连通图就是一个边双连通图。
一个割点可以属于多个点双。但是一条割边不属于任何边双。
我认为就是一个去了桥的图,原图连通不了了,可不就边双了。原图中有边双,那就说明新图的两部分想要到达彼此只有一条路线,那么,原图不是个好图,因为它不是个边双 /斜眼笑。
如果任意两点至少存在两条边不重复路径,则称该图为边双连通的。
所以,对于一个边双连通分量,里面任意两个点都至少存在两个边不重复的路径。
4.2 思路
首先,求出桥;
其次,dfs对于剩下的边染色;
最后,结束。
4.3 代码实现
inline void color(int x,int col){
Color[x]=col;
for(int i=head[x];i;i=a[i].next){
int v=a[i].to;
if(cutedge[i])continue;
if(!Color[v])color(v,col);
//没有判断v是不是fa,只是因为 ,fa在x之前已经被染过色了
}
}
inline void ebcc(){
for(int i=1;i<=n;i++)if(!Color[i])
++tot,color(i,tot);
}
5. 求无向图点双
5.1 定义
https://blog.csdn.net/qq_39670434/article/details/81973923
在一个无向连通图中,如果没有割点,那么它是点双连通图。
如果只有两个点,那么也是点双。一个割点可以属于多个点双。但是一条割边不属于任何边双。
对于任意一个点双连通分量,删除任意一点,连通性不变,但不一定是点双哟,孩纸。
5.2 思路
首先,找到那些割点,然后把它们所在的边扔进stack里;
然后,把剩下的图染色;
最后,结束。
5.3 代码实现
https://blog.csdn.net/fuyukai/article/details/51303292
这个代码肯定来自刘汝佳的蓝书,因为我当时也抄过,望天~
inline void Tarjan(int x,int fai){
dfn[x]=low[x]=++ind;
int son=0;
for(int i=head[x];i;i=a[i].next){
int v=a[i].to;
if(!dfn[v]){
stacki.push(i);
Tarjan(v,x);
low[x]=min(low[x],low[v]);
if(low[v]>=dfn[x]){
cutstain[x]=1;
++tot;sizei=0;//tot: the num of vbcc sizei: the num of this vbcc
vbcc[tot].clear();//vbcc: the vector of this bcc
while(1){
int k=stacki.top();stacki.pop();
if(belong[a[k].from]!=tot){
vbcc[tot].push_back(a[k].from);
belong[a[k].from]=tot;
}
if(belong[a[k].to]!=tot){
vbcc[tot].push_back(a[k].to);
belong[a[k].to]=tot;
}
if(a[k].from==x&&a[k].to==v)break;
}
}
if(x==root)++son;
}else if(fai!=v)
stacki.push(i),
low[x]=min(low[x],dfn[v]);
}
if(x==root&&son==1)cutstain[x]=0;//x is root,but it only has one son.
}
inline void vbcc(){
for(int i=1;i<=n;i++)if(!dfn[i])Tarjan(x,0);
}
posted on
浙公网安备 33010602011771号