P8436 【模板】边双连通分量 详细讲解

这篇具有很好参考价值的文章主要介绍了P8436 【模板】边双连通分量 详细讲解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

P8436 【模板】边双连通分量

概念

注意!双连通仅针对无向图而言。

  • 割边(桥):删去这条边使图不连通的边。

  • 边双连通图:不存在割边的图(等价定义:图中任意两个点都至少两条不同路径可以到达的图)。

  • 性质
    一个点不可能同时属于2个边双连通图,因为如果两个双连通分量相交与一点,那么删去任意一条边,两个子图之间仍然连通 , 故“属于同一个双连通图”的关系是具有传递性的。

  • 边双连通分量:一张连通图的极大边双连通子图

Tarjan 双连通分量求法

  • 从一个连通无向图内任意一点dfs,得到一棵深度优先遍历树,根据dfs第一次访问的顺序给每个点打上一个dfn标记,每个点的dfn唯一。
  • 同时使用low数组记录每个点能连回的点的最小dfn(初始为自己的dfn),如果一个点的low与其dfn相等则代表找到了一个双连通分量(这个点只能和它的子树上的所有点双连通,而没有另外一条边能使它到达它父节点及以上的点了)

如何通过图中的边正确地更新每个点的low?

  • 原图相对于这棵dfs生成树而言,多出的边可能有两种:

1. \(u-v\) 是祖先 - 子孙关系,即连接了在同一条“树链”上的两个点 (u,v的 LCA 要么是u,要么是v)

2. \(u-v\) 所在的子树没有相交,被称为“横叉边”,即横跨了两条“树链”(u,v的LCA既不是 \(u\) 也不是 \(v\)

  • 可以证明,对于一个双连通图dfs的生成树一定不存在“横叉边”:因为如果生成树有一条横叉边,在搜索到该边连接的2个节点的一个时,必然会把这条边作为自已子树的边继续dfs,而不会跳过这条边(补充:有向图的dfs生成树可以存在“横叉边”,因为先搜索搜索到横叉边连接的某个节点时,不一定可以通过这条边前往另一个节点(边指向先搜索到的边时))

  • 所以每找到一条新边u->v时只需要考虑其是否在树中:

  1. v没有被标记过 dfn,把 v 作为 u 的一棵子树搜索,递归完毕后用low[v]更新low[u]。

  2. v已经搜索过了,u - v 有子孙关系,v 一定在 u 所在的双连通分量内(因为在同一棵子树且已经有两条路径相连),所以直接用 dfn[v] 更新 low[u] ,用所有相连的v更新过一遍后一定可以保证 low[u] 的正确性,而不能用 low[v] 更新,因为“ X ”型图会导致更新出错,找到一个有割边的双连通图。

记录

  • 记录每个双连通图中的点(或每个点所在的双连通图):

    每dfs到一个新点就入栈,每找到一个双连通分量(dfn=low)就不断弹出直到当前栈顶为u,由于栈中顺序与dfs顺序相反,所以弹出的点都在一个双连通分量内。

  • 记录每条割边:

    找到图中一条生成树中不存在的边u->v并dfs完成v所在子树时,如果发现low[v]>dfn[u]即代表v没有第二条边与u相连了,u->v就是一条割边。

    对于第二种边(u->v且有子孙关系),由于在生成树中u与v已经有一条相连的路径,这条多出的边又是一条路径,所以这种边一定不可能是割边。

易错

对于有重边的图求双连通分量不能去重边,因为走另一条重复边回去也可以构成一条新路径,去重边可以导致漏掉很多双连通分量(如1<-><->2就是一个双连通图)。

这种情况就不能记录dfs前驱节点了(因为可以从重边回去),而需要记录走的上一条边,利用建图时同一条边的反向边是连续的性质,所以一条边异或1可以代表同一条无向边的反向边,如果发现新找到的边的返向边是来时的边就忽略。文章来源地址https://www.toymoban.com/news/detail-607259.html

Code

//P8436 【模板】边双连通分量

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<utility>
#include<vector>
using namespace std;
const int N=5e6+10;
int idx=2,h[N],e[N],ne[N],n,m;//因为后面调用tarjan的时候pre初始值是0,为防止第一条边就是1号边导致没法继续,所以idx从2开始 
void add(int a,int b)
{
	if(a==b) return;
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
vector<int> dcc[N];//每个边双连通分量里的点 
int dfn[N],low[N],cnt;//dfn:dfs顺序标记(时间戳),每个点唯一;low:每个点在当前所求边双连通分量里能返回的dfn最小的点
int bel[N],cntdcc;//记录所属的双连通分量(本题直接把同一个边双连通分量的所有点存在vector里,没有用到bel[] 
int st[N],top;//存当前找到的双连通分量的栈
void tarjan(int u,int pre)
{
	low[u]=dfn[u]=++cnt;//打时间戳标记,low初始为dfn 
	st[++top]=u;
	for(int i=h[u];~i;i=ne[i])
	{
		int v=e[i];
		//if(v==pre) continue; //错误 
		if((i^1)==pre) continue;//不能向自己的前驱寻找 //注意异或的优先级高,必须打括号 
		if(!dfn[v])//v没有访问过,这条边一定是dfs树上的边 
		{
			tarjan(v,i);//先dfs到最底层,再用子节点更新 
			low[u]=min(low[u],low[v]);//直接取子树的low
			/*记录割边 
			if(low[v]>dfn[u])//v点能到达的最浅的点还比u点深,故找到一条割边
				ans[anscnt++]=make_pair(min(u,v),max(u,v));
			*/
		}
		else low[u]=min(low[u],dfn[v]);//u,v有祖先-子孙关系(这条边一定不是横叉边)(可以证明) 
		//此时的v一定在u所在的边双连通分量内,所以用与u相连的所有的dfn[v]去更新low[u]一定可以确保low[u]的正确性
		//不能用low[v]更新,对于"X"型图可能漏掉割边导致求出的不是双连通图
	}
	if(dfn[u]==low[u])//自己就是连通块深度最低的点,找到一个双连通图(u的子树) (u不在这个双连通图内所以不弹出) 
	{
		int v=-1;
		cntdcc++;
		while(v!=u)
		{
			v=st[top--];
			bel[v]=cntdcc;
			dcc[cntdcc].push_back(v);
		}
	}
} 
int main()
{
	memset(h,-1,sizeof h);
	int a,b;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d%d",&a,&b),add(a,b),add(b,a);
	for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,0);
	printf("%d\n",cntdcc);
	for(int i=1;i<=cntdcc;i++)
	{
		printf("%d ",dcc[i].size());
		while(!dcc[i].empty()) printf("%d ",dcc[i].back()),dcc[i].pop_back();
		puts("");
	}
	return 0;
}

到了这里,关于P8436 【模板】边双连通分量 详细讲解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【图论】强连通分量进阶

    强连通分量可以判断环和进行缩点。还有一系列作用.... 这篇文章介绍 缩点 https://www.luogu.com.cn/problem/P2341 我们分析可以知道当一个点没有出度时,则为最受欢迎的牛。但如果有多个出度,则没有最受欢迎的牛。 这是只有一个出度的情况:   这是多个出度的情况: 但为什么要

    2024年02月14日
    浏览(36)
  • 【图论】强连通分量

    ​  强连通分量(Strongly Connected Components,简称SCC)是图论中的一个概念,用于描述有向图中的一组顶点,其中任意两个顶点之间都存在一条有向路径。换句话说,对于图中的任意两个顶点u和v,如果存在一条从u到v的有向路径,同时也存在一条从v到u的有向路径,那么u和v就属

    2024年02月14日
    浏览(29)
  • 有向图的强连通分量

    对于一个有向图,连通分量:对于分量中任意两点u,v,必然可以从u走到v,且从v走到u. 强连通分量:极大连通分量。 求出强连通分量后,可以通过将强连通分量缩点的方式,将有向图转化成有向无环图。 求强连通分量的方法:tarjan O(n+m),时间复杂度是线性的 1 . 采用dfs来遍历整

    2024年02月10日
    浏览(27)
  • 图的基本概念辨析,包括连通图、极大连通子图、连通分量、强连通图、极大强连通子图等

    概念(1-4)都是针对无向图的   图中从一个顶点到达另一顶点,若存在至少一条路径,则称这两个顶点是连通着的。例如图 1 中,虽然 V1 和 V3 没有直接关联,但从 V1 到 V3 存在两条路径,分别是 V1-›V2-›V3 和 V1-›V4-›V3,因此称 V1 和 V3 之间是连通的。 图 1 顶点之间的连

    2024年02月15日
    浏览(40)
  • 有向图的强连通分量算法

    有向图的强连通分量算法 强连通分量定义 在有向图中,某个子集中的顶点可以直接或者间接互相可达,那么这个子集就是此有向图的一个强连通分量,值得注意的是,一旦某个节点划分为特定的强连通分量后,此顶点不能在其它子树中重复使用,隐含了图的遍历过程和极大

    2024年02月06日
    浏览(46)
  • 第三章 图论 No.9有向图的强连通与半连通分量

    连通分量是无向图的概念,yxc说错了,不要被误导 强连通分量:在一个有向图中,对于分量中的任意两点u,v,一定能从u走到v,且能从v走到u。强连通分量是一些点的集合,若加入其他点,强连通分量中的任意两点就不能互相递达 半连通分量:在一个有向图中,对于分量中

    2024年02月13日
    浏览(32)
  • 团体程序设计天梯赛 L2-013 红色警报(连通分量)

    分数 25 战争中保持各个城市间的连通性非常重要。本题要求你编写一个报警程序,当失去一个城市导致国家被分裂为多个无法连通的区域时,就发出红色警报。注意:若该国本来就不完全连通,是分裂的k个区域,而失去一个城市并不改变其他城市之间的连通性,则不要发出

    2024年03月13日
    浏览(39)
  • idea如何设置注释模板,图文超详细讲解

    目录 先打开idea设置 一,idea类注释 1,找到以下设置 2,设置模板 3,apply保存完成 二,idea方法注释 1,创建自定义的组 2,创建模板 3,设置模板 4,选择生成模板的文件 5,绑定选择参数 6,完成ok 类注释模板和接口注释模板 方法注释模板 生成模板 /** +模板名+快捷键, 选择

    2024年02月14日
    浏览(35)
  • 并查集模板的应用:连通块

    837. 连通块中点的数量 给定一个包含 nn 个点(编号为 1∼n1∼n)的 无向图 , 初始时图中没有边 。 现在要进行 mm 个操作,操作共有三种: C a b ,在点 aa 和点 bb 之间连一条边,aa 和 bb 可能相等; Q1 a b ,询问点 aa 和点 bb 是否在同一个连通块中,aa 和 bb 可能相

    2024年02月14日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包