版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/83093461
点双联通分量
边双联通分量想必看这篇博客的同学就会,并且边双联通分量理解和打起来比较简单,就不再赘述了。
点双联通分量,类比边双的定义,它是原图的极大无向子图,满足删去子图中任意一个节点以及与其相邻的边,其余节点仍然连通。
如下图,左中两个均为一个点双联通分量,但最右边图中有两个点双联通分量(上下两部分),因为删去中间的点后他们不能联通
由最右边的图可以看出,一个点可能属于多个点双联通分量,我们称这些点为割点。
这也带来了麻烦,我们处理边双的时候,直接弹栈即可,但此时我们是否需要多一些讨论?
这就需要引入圆方树
圆方树
圆方树本来是一种用来处理仙人掌问题的数据结构
广义圆方树实际上就是将其拓展到了一般的无向图中,做起来差别不大,我们都可以统称为圆方树。
做题时,常常需要面对有关无向图中两点之间的简单路径(不经过重复点)的问题
无向图中的路径十分麻烦,圆方树可以将我们所需要的信息浓缩到一棵树上。
我们将原本无向图中的点称为圆点。
对于一个点双联通分量,我们建立一个方点来代表它,点双联通分量中的所有点都向这个方点连边。
我们将上面的这些图建成圆方树后,它长这样:
可以看出,圆方树中只存在圆点与方点之间的边。
构造圆方树,同样可以利用tarjan算法
有些模板是栈里是存储边的(可以看出,一条边只会出现在一个点双中),但存储点操作起来更加的方便
void tarjan(int k,int fa)
{
st[++st[0]]=k;
low[k]=dfn[k]=++dfn[0];
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa)
{
if(!dfn[p])
{
tarjan(p,k);
if(low[p]>=dfn[k])//栈中p所在的部分与k构成了一个点双
//当low[p]>dfn[k]时,这个点双只有一条边两个点。
{
lk(++n1,k),lk(k,n1);//新建方点n1,并向所有点双中的点连边
while(st[st[0]]!=p) lk(n1,st[st[0]]),lk(st[st[0]],n1),st[st[0]--]=0;
lk(n1,p),lk(p,n1),st[st[0]--]=0;
}
else low[k]=min(low[k],low[p]);
}
else low[k]=min(low[k],dfn[p]);
}
}
}
利用圆方树,我们避免了大量的讨论。
现在无向图中的路径问题变成了树上路径问题。
某些数据结构大神开发出了(仙人掌分治,虚仙人掌,仙人掌剖分等毒瘤玩意)。利用圆方树,这些问题都转化成树上的问题,好写又易于理解。