tarjan 系列之 “割点”
- 强连通分量、割点、割边、点双连通分量、边双连通分量的区分。
可能这个很多人搞不清楚。割点、割边(又称桥)、点双连通分量(简称点双)、边双连通分量(简称边双)四个都是无向图中的概念;强连通分量和 “缩点” 操作是有向图中的概念。
割点和点双通常一起出现,割边和边双通常一起出现。
“分量” 一般是定义子图的,比如点双连通分量是无向图的极大双连通子图。“xxx 分量” 和 “xxx 图” 在性质上一般没有区别。
- tarjan 系列怎么学?先学什么?
先学割点是没问题的,因为这些你学会一个其它的也都会了。从难度角度,本人也认为割点在 tarjan 家族难度不高,至少比强连通分量好理解。
- 割点、点双的定义?
一个无向图如果去掉某个节点和它的所有边就不再连通,那么这个点叫做割点;
如果一个无向图去掉任意一个节点都连通,即不存在割点,那么这个图叫做点双连通图;
一个无向图中的每个极大点双连通子图称作此无向图的点双连通分量。
说白了,割点就是删掉后使图从连通变成不连通的点,在点双中不存在割点。
割边和边双的定义其实可以类比。
- 点双和边双的关系有什么特殊性?
首先,很显然,点双、边双一定是一个连通块。一个连通块不一定是点双、边双。
其次,除两点一线的特殊情况,点双一定是边双,反之不一定。
- 怎么求割点?
正片开始。P3388 【模板】割点(割顶)
以下说的是连通图,不连通分别做就行。
我们要接触到 tarjan 家族的老朋友:
- dfs 树:对一个无向连通图
G=(V,E) 做一遍 dfs,经过的点和边构成一个树G'=(V,E') ,这棵树叫做 dfs 树。
这样,我们可以对边集
因为 dfs 到了一个节点会走完能走的边,我们能注意到,每条非树边两边的节点在 dfs 树中一定连接了祖先和子孙,我们把这种边叫做返祖边。不存在没有祖先子孙关系的非树边。也就是说,假设一条边
那么这两个数组怎么算?首先,第一次搜到
扫
接下来看割点怎么判断。
对于非根节点
现在讲为什么返祖边的情况里
对于根节点,因为它的
void tarjan(int x)
{
int child=0;
dfn[x]=low[x]=++cnt;
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
if(x==root)child++;
if(dfn[x]<=low[y]&&x!=root)
IsCut[x]=1;
}
else low[x]=min(low[x],dfn[y]);
}
if(x==root&&child>=2)
IsCut[x]=1;
}
int main()
{
rd(n,m);
for(int i=1;i<=m;i++)
{
int u,v;
rd(u,v),add(u,v),add(v,u);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) root=i,tarjan(i);
int ans=0;
for(int i=1;i<=n;i++)
if(IsCut[i]) ans++;
wt(ans,'\n');
for(int i=1;i<=n;i++)
if(IsCut[i]) wt(i,' ');
return 0;
}
有问题评论指出,随时解释 or 改正。