首页 > 其他分享 >图的连通性小记

图的连通性小记

时间:2024-09-14 18:46:58浏览次数:8  
标签:连通 连通性 int 割点 割边 dfn low 小记

前言

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 树

标签:连通,连通性,int,割点,割边,dfn,low,小记
From: https://www.cnblogs.com/hutongzhou/p/18409018

相关文章

  • 连通性问题(有向图)(未完结)
    强连通分量我们首先定义两种边:返祖边为从一个点指向其祖先的边;横叉边从某个点指向树中另一个子树中的点的边。两者统称为非树边。而剩下的边即为树边,树边也就是在\(dfs\)树上的边。我们定义\(dfn_i\)为\(i\)是第几个被\(dfs\)到的,\(low_i\)从\(i\)出发走任意条边,但是......
  • 小董小记——9.10教师节人工智能教育技术学小记(1)
    下午蒙蒙小雨,也抵挡不住我们开拓知识技能的热情。我们也算是使用博客园的初学者,也在逐步学会通过使用博客园发布自己的一些小随笔,生活学习的小记录。说实话,我在没使用过博客园之前,都是从电视剧里面或者别人口中听来的,所以,我一直以为它是微博的一个分身。结果,并不是。多学习一项技......
  • 24.9.10教师节课堂小记
    一、符号1、使用“—”符号在关键词的前面使用减号,也就意味着在查询结果中不能出现该关键词,“关键词A+空格+减号+关键词B”2、使用FILETYPE\SITE和INTITLE指令:使用filetype指令可以查询特定格式的文件,比如doc\txt\ppt\pdf,搜索格式为:关键词+空格+filetype:+文件格式使用site指......
  • 组合数小记
    前言计数的基本原理考虑一个集合:\(S\),求\(|S|\)。加法原理:\(S=S_1\cupS_2,|S|=|S_1|+|S_2|\)。乘法原理:\(|{(a,b)|a\inS_1,b\inS_2}|=|s_1||s_2|\)更浅显的说当两件事情无关时为加法,当前一件的结果影响后面时用乘法。组合数基本公式及衍生公式排列与组合排列......
  • WGCLOUD使用指南 - 监测数据库的连通性
    数据可视化监测是WGCLOUD的一个重要模块,可以帮我们监控数据源是否在线,自定义sql查询数据进行可视化展示,比如新增订单、注册用户量、数据库运行参数等信息数据监控是由server来监测的,因此要保证server主机能够访问到数据库如果server无法访问被监控的数据源,怎么监控 1、......
  • 策略模式的小记
    策略模式策略模式支付系统【场景再现】硬编码完成不同的支付策略使用策略模式,对比不同(1)支付策略接口(2)具体的支付策略类(3)上下文(4)客户端(5)小结策略模式定义:策略模式是一种行为设计模式,在运行时改变对象的行为。目的:定义一系列算法,把它们一个个封装起来,并且使它们可相......
  • 图论连通性相关
    并查集普通并查集:路径压缩写法:structUnion_Find_Set{ intf[N]; inlinevoidinit(){ for(inti=1;i<=n;++i) f[i]=i; } inlineintfind(intx){ if(x!=f[x])f[x]=find(f[x]); returnf[x]; } inlinevoidmerge(inta,intb){ intx......
  • 数位DP小记
    1.基础1.1.问题数位DP解决的一般都是和数字相关的计数问题,常见的有\(l\simr\)中有多少数符合某个关于数位的条件。对于这种问题,我们都是先用前缀和转化成小于等于某个数的问题。下面以P2602[ZJOI2010]数字计数为模板题。1.2记忆化搜索我们先枚举每个数码。我们......
  • 快一年没进这里了,小小记录下吧
    (1)进了某上市股份银行的数仓迁移项目(2)遇到了相似又不同的问题,就是所谓的项目经理排挤式,看来我是天生招小人的体质,需要接种疫苗(3)乙方的管理水平,刷新了我的三观,本以为遇到中行甲方的管理水平下限,没想到我离开这个行业之前,还能让我领教到乙方的管理水平下限(4)充分再次理解什么叫乱叫......
  • 回文自动机小记
    构建口胡一下过程:\(fail\)边指向自己的最长回文后缀(偶根指向奇根)。定理:每添加一个字符,至多新增一个新的本质不同的回文串,且是所有回后缀中最长的。由此得出推论:本质不同的回文子串(回文自动机的点数)不超过|S|暴力跳终止链,找到第一个左侧有\(x\)的回文后缀\(v\)。......