图的连通性小记

news/2024/11/17 17:46:56/文章来源:https://www.cnblogs.com/hutongzhou/p/18409018

前言

DFS 树

无向图 DFS 树

定义:DFS树 是在图或树结构上进行深度优先搜索时形成的树。在 DFS 过程中,从一个顶点开始,尽可能深地搜索图的分支,直到达到一个没有未访问邻居的顶点,然后回溯到上一个顶点继续搜索。

从点 \(r\) 开始搜索,每次进入一个点 \(i\) 对应的边 \((fa_i,i)\) 为树边,其他的为回边。

img

图来源标于参考资料中。

有向图 DFS 树

对于若连通图,从一个点搜索不一定能搜到所有节点,一般是以森林的形式。

选择一个未访问的点,按无向图的方式建树,直到无法搜索。

每一棵子树如下图:

img

图片来源

一些同无向图不一样的边:

  • 从祖先指向后代的非树边,称为 前向边

  • 从后代指向祖先的非树边,称为 返祖边(后向边)

  • 两端无祖先后代关系的非树边,称为 横叉边

性质

  1. 在生成树种,图的回边连接的都是一个顶点和它的子孙节点。

  2. 当且仅当树边 \((u,v)\) 不存在连接其祖先和子孙节点的回边时,它是割边(桥)。

无向图连通性

割点与割边

定义

  1. 割点:在无向图中,删去后使得连通分量数增加的点称为 割点

  2. 割边:在无向图中,删去后使连通分量数增加的边称为 割边,也称为

  3. 将某种类型的连通分量根据等价性或独立性缩成一个点的操作称为 缩点,原来连接两个不同连通分量的边在缩点后的图上连接对应连通分量缩点后形成的两个点。

  4. 必经点的定义:在从点 \(u\) 到点 \(v\) 的所有路径中,所有路径都必须经过的点。

  5. 两点边双连通是指它们在同一个边双内。两点点双连通是指它们在同一个点双内。

性质

边双的性质:

  1. 两点之间任意一条迹上的所有割边,就是两点之间的所有必经边。

  2. 边双和点双缩点后均得到一棵树,而强连通分量缩点后得到一张有向无环图。

  3. 如果在原图上新连一条边,设为:\((u,v)\),那 \(u,v\) 所在边双的树上简单路径会被缩成一个大边双,所以边双具有传递性,即:若 \(a,b\) 双连通,\(b,c\) 双连通,那么 \(a,c\) 双连通。

  4. 两个点 \(u,v\) 双连通,当且仅当 \(u,v\) 之间没有必经边(割边)。

  5. 一条边双中的边 \((u,v)\),删去后 \(u,v\) 依然联通,将此路径与边 \((u,v)\) 相接得到不经过重复边的回路,所以对于边双内任意一条边 \((u,v)\),存在经过 \((u,v)\) 的回路。

推论:

  • 对于边双内任意一条边 \((u,v)\),存在不经过重复边的回路。
  • 对于边双内任意一个点 \(u\),存在经过 \(u\) 的回路。

点双的性质:

  1. 删去割点后不连通的两个点之间任意一条路径必然经过该割点,这割点为必经点。两点之间的所有割点 不一定是 两点之间的必经点,因为割点的定义是关于图的整体结构的,而必经点是针对具体的两点之间的所有路径。如果一个割点不位于两点之间的所有路径上,它就不是这两点之间的必经点。

  2. 点双两两可能有交,所以点双不具有传递性。

  3. 若两点双有交,那么交点一定是割点。因为如果点双有交,这个焦点一定阻碍了点双的扩大:删去此点依然联通,点双会扩大。

  4. 点双的交点是割点,但割点不一定是点双的交点,是一个点属于超过一个点双。

  5. 一条边恰属于一个点双。

可以得出定义:可知割点是连接点双的桥梁,正如割边是连接边双的桥梁。用一个点代表一个点双,并将点双代表点向它包含的割点连边,得到 块割树(Block-Cut Tree,“点双连通块 - 割点” 树)。

  1. 对于一个 \(n>3\) 的点双,其中的点 \(u\) 存在经过 \(u\) 的简单环。

menger 定理

  1. 边形式:对于有限图中的任意两个不同顶点 \(u\)\(v\),它们之间的最大相互不相交路径的数量等于为了使 \(u\)\(v\) 断开连接而必须删除的最小顶点数。

  2. 点形式:对于无向图上任意不同且不相邻的两点 \(u,v\),使得 \(u,v\) 不连通所需删去的点的数量的最小值,等于 \(u,v\) 之间点不相交(不在除了 \(u,v\) 以外的点相交)的路径数量的最大值。

求割点

这里把 alex_wei 的话简单的复述一遍。

先构建出图的 DFS 树。

现在探究点 \(x\) 是否为割点,设 \(T(x)\) 为根为 \(x\) 的树,\(T'(x)\) 为图除去 \(T(x)\) 的部分,\(y\in T(x)\)。如果除去 \(x\)\(y\)\(T'(x)\) 中的节点都不相连,那么 \(x\) 就是割点。

所以现在问题变为:不经过 \(x\) 能到达的所有点都属于 \(T(x)\)

设以点 \(y\) 为根的子树为 \(T(y)\),点 \(u \in T(y)\),点 \(v \in T'(x)\)。设存在一条路径使得 \(y\) 不经过 \(x\) 到达 \(T'(x)\)\((u,v)\) 为跨越 \(T(y)\)\(T'(x)\) 的一条边,可以知道 \(v\) 一定是 \(x,u\) 的祖先。

img
图来源标于参考资料中

可以知道不管是 \(y,u\) 一定都在除 \(x\)\(x\) 为根的树内,所以割点的判断条件为除 \(x\) 的以 \(x\) 为根的子树内存在一个点 \(u\),连接到 \(x\) 的祖先。

\(f_x\) 表示 \(x\) 通过非树边(回边)相连的点的 \(dfn\) 的最小值。

所以定义了 \(low_x\) 函数,表示 \(T(x)\)\(f_x\) 的最小值。

所以非根结点的割点判定法则:

\(x\) 为割点当且仅当存在 \(u\) 属于除 \(x\) 的以 \(x\) 为子树中,且 \(low_u \le dfn_x\) 即不存在 \(x\) 的祖先与 \(u\) 相连。

如果是根节点,如果根节点的儿子数大于一,即 \(son_x \ge 2\),可以把树分为几个联通块,所以是割点。

模板题 code:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
vector<int>e[N];
int n,m;
int dfn[N],low[N],buc[N],tim;
void tarjan(int u,int fa)
{dfn[u]=low[u]=++tim;int son=0;for(auto v : e[u]){if(v==fa)   continue;if(!dfn[v]){son++,tarjan(v,u);low[u]=min(low[u],low[v]);if(low[v]>=dfn[u]&&fa!=0)   buc[u]=1;}else low[u]=min(low[u],dfn[v]);}if(son>=2&&fa==0)   buc[u]=1;
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int u,v;scanf("%d%d",&u,&v);e[u].push_back(v),e[v].push_back(u);}for(int i=1;i<=n;i++){if(!dfn[i]) tarjan(i,0);}int ans=0;for(int i=1;i<=n;i++)   ans+=buc[i];printf("%d\n",ans);for(int i=1;i<=n;i++)if(buc[i])  printf("%d ",i);return 0;
}

求割边

与割点差不多。

可以知道割边一定是树边,回边一定不是割边。

设一条边为 \((u,v)\)\(u\)\(v\) 的祖先,这条边为割边当且仅当 \(low_u<dfn[v]\)

注意判树边的方法,一般图论题都会用 if(fa==v) continue;,但如果树边是重边就不好判断了。

如果用链式前向星,把 cnt=1 每条边的编号为 \(k,k+1\),把当前编号异或一得到反边,如果用 vector 存图,开个 pair 存一下边的编号就可以了。

缩点只用开个栈,每次找到一个割边 \((u,v)\) 就从 \(u\) 一直出栈,直到 \(v\),这样一个边双就缩成一个点。其他的缩点方式类似。

模板题 code:

#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N=1e6+10;
int n,m;
vector<pii>e[N];
int tim,dfn[N],stk[N],top,low[N],tot;
vector<int>ans[N];
void form(int u)
{++tot;do{ans[tot].push_back(stk[top]);}while(stk[top--]!=u);
}
void tarjan(int u,int fa)
{dfn[u]=low[u]=++tim;stk[++top]=u;for(auto t : e[u]){if(t.second==fa)    continue;int v=t.first;if(!dfn[v]){tarjan(v,t.second);low[u]=min(low[u],low[v]);if(dfn[u]<low[v])   form(v);}else low[u]=min(low[u],dfn[v]);}   
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int u,v;scanf("%d%d",&u,&v);e[u].push_back({v,i}),e[v].push_back({u,i});}for(int i=1;i<=n;i++)if(!dfn[i]) tarjan(i,0),form(i);printf("%d\n",tot);for(int i=1;i<=tot;i++){printf("%d ",ans[i].size());for(auto v : ans[i])    printf("%d ",v);puts("");}return 0;
}

有向图联通性

定义

强连通:对于有向图上两点 \(u,v\) 如果两点可以互相到达,称两点强连通。

强连通图:满足任意两点强连通的有向图称为 强连通图。它等价于图上任意点可达其它所有点。

强连通分量:有向图的极大强连通子图称为 强连通分量(Strongly Connected Component,SCC)。

性质

前文可知,无向图的 DFS 树还有返祖边、横叉边与前向边。如果用时间戳记录搜索顺序,返祖边与横叉边是指向时间戳比他早的节点,而前向边不是,根据上文无向图的经验,应该探讨返祖边与横叉边。

  1. \(u,v\) 强连通,\(u,v\) 树上路径上的所有点都强连通。

  2. \(f_x\)\(x\) 的所有 \(x\) 的返祖边、横叉边 \((u,v)\)\(u\) 的最小时间戳,\(x\) 是关键点(某个 scc 的最浅节点),当且仅当 \(f_x \ge dfn_x\)

tarjan 求 scc

\(f_x\) 的定义与上文的无向图大致相同,得到关键点的方法与求割点大致相同,所以考虑如何求 scc。

为了找到最浅节点,也就是使强连通子图最大,选取的关键点是:\(f_x=dfn_x\)

要得到这个子图,用栈维护,搜到一个点就加入栈,找到关键点就回退栈。

模板 code:


缩完点之后的图是 DAG,点的编号的逆序满足拓扑序。

再探 DFS 树

树上差分求割边

结论:当且仅当树边 \((u,v)\) 不存在连接其祖先和子孙节点的回边时,它是割边。也就是说,树边 \((u,v)\) 是桥当且仅当此时没有回边跨越它。

所以求割点的方法为:

  1. 建立这张图的 DFS 树。

  2. 对每一条树边 \((u,v)\),寻找是否有一条回边跨越 \((u,v)\),如果没有,它就是割边。

对于第二条,可以再建树时判断哪些时回边,并知道祖孙关系,然后标记两点,做个差分即可。

无向图加方向转为强连通图

先判断能否转为强连通图:如果图中有割边,则一定无法构成。

现在图是一个大的边双,又知道边双中任意一点都存在回路,所以只用在割点判断时记录路径即可。

后言

参考资料:

  1. alex_wei:图论1。

  2. DFS 树

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/797147.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端项目通过 Nginx 发布至 Linux,并通过 rewrite 配置访问后端接口

本文通过将 arco 框架的前端项目,部署至 CentOS 7,并访问同服务器的 WebAPI 接口,来简单演示一下,如何将前端项目发布至 Linux 系统。〇、前言 本文通过将 arco 框架的前端项目,部署至 CentOS 7,并访问同服务器的 WebAPI 接口,来简单演示一下,如何将前端项目发布至 Lin…

练习第四周8.31

作业: 1、安装burp并实现抓取HTTP站点的数据包(HTTPS站点暂时不要求)2、练习Tomcat PUT方法任意写文件漏洞(CVE-2017-12615),提供蚁剑连接成功截图3、练习S2-048 远程代码执行漏洞(CVE-2017-9791),提供命令执行截图 4、练习JBoss 5.x/6.x 反序列化漏洞(CVE-2017-1214…

【csp201912-2】回收站选址

题目背景开学了,可是校园里堆积了不少垃圾杂物。热心的同学们纷纷自发前来清理,为学校注入正能量~ 题目描述 通过无人机航拍我们已经知晓了n处尚待清理的垃圾位置,其中第i(1≤i≤n)处的坐标为(x,y),保证所有的坐标均为整数。我们希望在垃圾集中的地方建立些回收站。具体来说…

【Ehviewer绿色版】1.9.8.4最新版本下载2024安卓苹果

Ehviewer 是一款主要用于浏览和下载漫画、插画等二次元图像内容的软件。适用安卓和苹果系统,Ehviewer拥有海量的漫画作品,涵盖各种题材和风格,包括日本漫画、韩国漫画、欧美漫画以及国内的一些同人创作等。无论是热门的商业漫画还是小众的独立作品,都能在 Ehviewer上找到,…

uniapp - uView 组件库的u-button 不支持 @click.stop事件,会报错 - 解决

包一层view即可,点击事件不要写在按钮上 本文来自博客园,作者:岑惜,转载请注明原文链接:https://www.cnblogs.com/c2g5201314/p/18414452响应开源精神相互学习,内容良币驱除劣币

章13——常用类——包装类,Integer类

包装类ctrl + b 可以跳转源代码。char 和 boolean的继承体系:包装类和基本数据的转换//装箱int n = 200;Integer integer = n;//拆箱int n1 = integer;包装类练习题三元运算符中是一个整体,其中精度最高的是double,所以无论结果返回什么,都会提高obj1的精度。 包装类到Stri…

大模型应用开发初探 : 手搓一个简易Agent

本文简单介绍AI Agent的基本概念 和 工作方式,目前主要有两种开发Agent的模式,一种是高代码手搓,另一种是低代码拖拉拽。然后,通过C# + Semantic Kernel + 智谱LLM模型 演示了如何快速开发一个简易的AI Agent,虽然它只是个Demo,但希望对你快速了解Agent有所帮助!大家好,…

安装vCenter VCSA 7.0 报错 Failed to run vdcpromo 的问题

百度了一下说是DNS的问题,但我也设置了8.8.8.8或电信的dns,都不行。外网找了一下说要设置为127.0.0.1,但是7.0U3a后的版本不允许填写127.0.0.1了。最后找到一个通过CLI的方式安装可以避免这个问题。 首先创建一个CLI的横版文件,内容如下: {"__version": "2…

PostSync介绍

PostSync 促进技术文章发展介绍 这是一个开源的同步文章的软件,你可以使用它来同步你的文章到多个平台。 使用打开浏览器,登录各个平台的账号,掘金、CSDN、知乎、公众号、哔哩哔哩、博客园、个人WordPress 打开config.yaml文件,配置你的浏览器信息以及浏览器用户数据目录 运…

【linux】centos7安装8.4.2版本mysql

1、前置: 清除mysql相关数据rpm -qa | grep mariadbrpm -e --nodeps 查出来的文件名rpm -qa | grep mysqlrpm -e --nodeps 查出来的文件名2、安装mysql依赖包(没试过不装会有什么问题)# 查找libaio [root@node2 ~]# rpm -qa|grep libaio# 安装libaio [root@node2 ~]# yum -y…