OI-wiki 图论专题:无向图双连通分量(边双 / 点双)的 Tarjan 与差分算法全解析

发布时间:2026/9/12 3:49:42

OI-wiki 图论专题:无向图双连通分量(边双 / 点双)的 Tarjan 与差分算法全解析 OI-wiki 图论专题无向图双连通分量边双 / 点双的 Tarjan 与差分算法全解析【免费下载链接】OI-wiki:star2: Wiki of OI / ICPC for everyone. 某大型游戏线上攻略内含炫酷算术魔法项目地址: https://gitcode.com/GitHub_Trending/oi/OI-wiki导读双连通分量是图论与 OI/ICPC 竞赛中的核心概念之一它刻画了无向图在删除单条边或单个点后仍能保持连通性的冗余结构。本文以 OI-wiki 的 双连通分量文档 为主体结合仓库内 bcc 目录 的四份可直接运行的 C 实现与 配套测试数据系统讲解边双连通分量E-BCC与点双连通分量V-BCC的定义、DFS 生成树性质以及三种主流求解思路两种 Tarjan 算法和基于树上差分的算法。读完后你将掌握判定桥、割点与双连通分量的完整理论并能对照源码写出可提交的模板代码。阅读本文前建议先了解 图论相关概念并配合阅读 割点和桥 与 强连通分量 章节。基本定义边双连通与点双连通在一张连通的无向图中对于两个点 $u$ 和 $v$如果无论删去哪一条边只能删一条都不能使 $u,v$ 不连通称 $u$ 和 $v$边双连通如果无论删去哪一个点只能删一个且不能删 $u$、$v$ 自己都不能使 $u,v$ 不连通称 $u$ 和 $v$点双连通。割点与桥的更严谨定义参见 图论相关概念 与 割点和桥。两个值得注意的性质边双连通具有传递性若 $x,y$ 边双连通$y,z$ 边双连通则 $x,z$ 也边双连通。因此边双连通关系是等价关系可以直接用来划分连通块。点双连通不具有传递性下图中 $A,B$ 点双连通$B,C$ 点双连通但 $A,C$ 并不点双连通——因为删除 $B$ 后 $A$ 与 $C$ 即被分开。基于上述定义无向图中的极大边双连通子图称为边双连通分量Edge-Biconnected Component无向图中的极大点双连通子图称为点双连通分量Vertex-Biconnected Component也叫块Block。DFS 生成树分析无向图连通性的基本工具对于一张连通的无向图从任意一点开始 DFS 可以得到原图的一棵 DFS 生成树以起点为根。生成树上的边称为树边不在生成树上的边称为非树边。由于 DFS 的访问顺序栈式遍历性质可以保证所有非树边连接的两个点在生成树上满足其中一个是另一个的祖先。这一性质是后续所有算法的基石——非树边只会从祖先指向后代因此每一条非树边都唯一对应树上的一条由树边构成的简单路径。最朴素的 DFS 遍历框架如下void DFS(int p) { visited[p] true; for (int to : edge[p]) if (!visited[to]) DFS(to); }def DFS(p): visited[p] True for to in edge[p]: if visited[to] False: DFS(to)边双连通分量E-BCC以洛谷 P8436【模板】边双连通分量为例给定 $n$ 个节点、$m$ 条无向边的图需要输出边双连通分量的个数以及每个分量内的顶点集合。下面给出三种求解方法时间复杂度均为 $O(nm)$。Tarjan 算法 1先求桥再 DFS 划分思路分为两步用 Tarjan 求出图中所有桥求桥的方法见 割点和桥 的桥部分删掉所有桥之后图中剩下的每个连通块就是一个边双连通分量再 DFS 一遍即可划分。判定桥的核心条件是 $low_v dfn_u$对于树边 $u\to v$这与求割点的条件 $low_v \ge dfn_u$ 恰好差一个等号当 $v$ 无法通过非树边回到 $u$ 或更早的祖先时$u-v$ 这条边就是桥。仓库中的 bcc_1.cpp 是本题的完整实现。关键点如下使用链式前向星存无向边tot从 1 开始这样边i的反向边恒为i ^ 1便于在 DFS 中跳过来时的边i ! (in ^ 1)从而正确处理重边tarjan中当dfn[x] low[v]时把bz[i]与bz[i ^ 1]同时标记为桥第二遍dfs从每个未被分组的点出发遇到桥边bz[i]为真就停止扩展从而把分量完整切分出来。void tarjan(int x, int in) { dfn[x] low[x] bcc_cnt; for (int i hd[x]; i; i e[i].nt) { int v e[i].to; if (dfn[v] 0) { tarjan(v, i); if (dfn[x] low[v]) bz[i] bz[i ^ 1] true; // (x,v) 是桥 low[x] min(low[x], low[v]); } else if (i ! (in ^ 1)) low[x] min(low[x], dfn[v]); } } void dfs(int x, int id) { vis_bcc[x] id, bcc[id - 1].push_back(x); for (int i hd[x]; i; i e[i].nt) { int v e[i].to; if (vis_bcc[v] || bz[i]) continue; // 桥边不能跨过 dfs(v, id); } }主函数中对每个未访问点调用tarjan(i, 0)即可处理不连通图输出时先打印分量个数再逐行输出每个分量的大小与顶点编号。仓库中 bcc_1.in 与 bcc_1.ans 提供了可验证的样例5 点 8 边的图含重边与自环应输出 1 个包含全部 5 个点的边双连通分量。Tarjan 算法 2类比强连通分量无向图中 DFS 生成树上的边不是树边就是非树边这给了我们一个更简洁的思路在无向图中只要一个分量没有桥那么在 DFS 生成树上它的所有点都在同一个强连通分量中反过来DFS 生成树上的一个强连通分量在原无向图中就是边双连通分量。因此求边双连通分量的过程实际上就是在无向图上跑一遍求强连通分量的 Tarjan。仓库中的 bcc_2.cpp 正是这个思路维护一个栈st当dfn[u] low[u]时把栈顶到 $u$ 的部分弹出一个分量。与有向图版本唯一的区别是用来时的边编号i (in ^ 1)跳过代替父节点判断从而保证无向边不被反向边干扰。void tarjan(int u, int in) { low[u] dfn[u] bcc_cnt; st.push(u), vis[u] true; for (int i hd[u]; i; i e[i].nt) { int v e[i].to; if (i (in ^ 1)) continue; if (!dfn[v]) tarjan(v, i), low[u] min(low[u], low[v]); else if (vis[v]) low[u] min(low[u], dfn[v]); } if (dfn[u] low[u]) { // 弹出强连通分量即原图的边双连通分量 vectorint t; t.push_back(u); while (st.top() ! u) t.push_back(st.top()), vis[st.top()] false, st.pop(); st.pop(), ans.push_back(t); } }这一算法的正确性依赖于无桥分量内任意两点可互相到达这一事实从源码结构看它省去了显式求桥与二次 DFS实现更紧凑。差分算法非树边覆盖与树上差分先看一张示意图图中黑色与绿色边为树边红色边为非树边。每条非树边的两个端点唯一对应树上一条由树边构成的简单路径称这条非树边覆盖了该路径上的所有边。具体来说绿色的树边至少被一条非树边覆盖黑色的树边不被任何非树边覆盖。于是得到关键结论非树边与被至少一条非树边覆盖的树边一定不是桥未被任何非树边覆盖的树边一定是桥。暴力的做法是枚举每条非树边、逐条把覆盖的树边标记为绿复杂度 $O(nm)$。优化方式是用树上差分对每条非树边在其树上深度较大的端点打1标记深度较小的端点打-1标记$O(n)$ 求出每个点子树内部的标记和对点 $u$子树标记和等于覆盖 $u$ 与 $fa_u$ 之间树边的非树边数量若该值为 $0$则 $u$ 与 $fa_u$ 之间的树边是桥最后再 DFS 一遍不跨过桥边即得边双连通分量。仓库中的 bcc_4.cpp 给出了完整实现其中有两个值得注意的工程细节用vector实现简易哈希表re判重边、be记桥键为min(x,y) * N max(x,y)的哈希编码因为题目时空限制较紧源码注释里也给出了不紧时可用的mappairint,int, int替代方案dfs先算深度与差分值dfs2自底向上累加子树和并判定桥dfs3沿非桥边划分分量。int dep[N], bz[N], sum[N]; // 深度、单点差分值、子树差分和 void dfs(int x, int pre) { // 计算深度与单点差分 if (dep[x] dep[pre]) bz[x], bz[pre]--; // 回到祖先更新差分 if (dep[x]) return; dep[x] dep[pre] 1; for (int i hd[x]; i; i e[i].nt) dfs(e[i].to, x); } int dfs2(int x, int pre) { // 处理子树差分和sum[x] 0 即 (x,pre) 是桥 if (vis[x] 1) return sum[x]; vis[x] 1, sum[x] bz[x]; for (int i hd[x]; i; i e[i].nt) { int v e[i].to; if (dep[v] dep[x] !vis[v]) sum[x] dfs2(v, x); } if (sum[x] 0 re[P(x, pre)] 1) be[P(x, pre)] 1; return sum[x]; }扩展应用CEOI2015 Day1「管道」——16MB 内存下的求桥问题本题要求对一张 $N$ 点 $M$ 边、不保证连通的无向图求每个连通块视为子图中的所有桥但内存只有 16 MB。题解的关键观察是既然存不下所有边就考虑优化存边——若一条非树边被另一条非树边完全覆盖则这条边是无用的去掉它不影响桥的判定。可以用并查集维护将所有非树边按对应路径长度从短到长处理用并查集把已被覆盖的树边跳过从而只保留必要的边信息在极低内存下完成求桥。这展示了差分思想在空间受限场景下的变体应用。点双连通分量V-BCC以洛谷 P8435【模板】点双连通分量为例输出点双连通分量的个数以及每个分量。需要先学习割点的判定参见 割点和桥 的割点部分。Tarjan 算法点双连通分量的 Tarjan 算法基于以下两条性质两个点双最多只有一个公共点且该公共点一定是割点——这正是点双之间通过割点串联的结构对于一个点双它在 DFS 搜索树中 $dfn$ 值最小的点一定是割点或者树根。根据性质 2 分类讨论当这个点是割点时它一定是所在点双连通分量的根因为一旦包含它的父节点它仍然是割点分量就无法再极大了当这个点是树根时有两个及以上子树则它是割点只有一个子树则它是该子树的一个点双连通分量的根没有子树孤立点视作一个单独的点双。仓库中的 bcc_3.cpp 是完整实现。核心流程DFS 过程中维护栈sta当发现low[v] dfn[u]$v$ 是 $u$ 的儿子且不能绕回 $u$ 上方时说明 $u$ 是一个割点候选此时从栈顶弹出直到弹出 $v$再压入 $u$即构成一个点双同时用f 1 || u ! root判定 $u$ 是否为割点。对孤立点u root hd[u] 0直接单独形成一个分量。void tarjan(int u) { dfn[u] low[u] bcc_cnt, sta[top] u; if (u root hd[u] 0) { // 孤立点单独一个点双 dcc[cnt].push_back(u); return; } int f 0; for (int i hd[u]; i; i e[i].nt) { int v e[i].to; if (!dfn[v]) { tarjan(v); low[u] min(low[u], low[v]); if (low[v] dfn[u]) { // u 是 v 所在点双的底部割点 if (f 1 || u ! root) cut[u] true; cnt; do dcc[cnt].push_back(sta[top--]); while (sta[top 1] ! v); dcc[cnt].push_back(u); // 割点本身同时属于相邻的多个点双 } } else low[u] min(low[u], dfn[v]); } }注意与边双不同割点会被同时计入它所属的多个点双连通分量这正是两个点双至多共享一个割点性质的直接体现。差分算法蓝点图点双同样有基于差分的做法。如上图黑色边为树边红色边为非树边每条非树边对应树上一条由树边构成的简单路径。构造一张新图新图中的每个点对应原图中的每一条树边图中用蓝色点表示对原图中的每条非树边把它对应路径上的所有树边在新图中对应的蓝点连成一个连通块图中用蓝色边体现。在这个新图模型下成立如下判定一个点不是割点当且仅当与其相连的所有边在新图中对应的蓝点都属于同一个连通块两个点点双连通当且仅当它们在原图树上的路径中的所有边在新图中对应的蓝点都属于同一个连通块——即图中的每个蓝点连通块都对应一个点双连通分量。蓝点间的连通关系可以用与求边双时相同的差分技巧维护路径覆盖 子树求和整体时间复杂度 $O(nm)$。其思想本质是把点的连通性问题转化为边蓝点的连通性问题从而复用差分的高效性。算法对比与选型建议算法求解对象核心思想实现要点仓库源码Tarjan 算法 1边双先求桥删桥后 DFS 划分链式前向星i^1处理反向边与重边bcc_1.cppTarjan 算法 2边双无向图强连通分量即边双栈 dfnlow弹栈bcc_2.cpp差分算法边双非树边覆盖 树上差分判桥子树差分和、哈希判重边bcc_4.cppTarjan 算法点双割点性质 栈式划分割点同时归属多个分量bcc_3.cpp差分算法点双边转点蓝点连通块差分维护路径覆盖理论见本文实际竞赛中只需判桥/求边双且图较大时Tarjan 算法 1 或 2 最直接Tarjan 算法 2 代码更短需要边双内部的边集或对桥做进一步缩点处理时算法 1 的桥标记天然可用内存受限或需要对路径覆盖做批量处理时差分算法bcc_4.cpp 的思路更优CEOI2015「管道」即典型场景求点双块并处理割点时必须用点双专属的 Tarjan 实现bcc_3.cpp注意割点会出现在多个分量中。四份代码均配套有 输入输出样例例如 bcc_1.in 构造了含重边2 4与自环1 1的 5 点图bcc_1.ans 验证了其边双分量应为全部 5 个点——重边与自环都不影响边双连通性可直接用于自测你的实现。小结边双连通具有传递性、可按等价类划分点双连通不具传递性分量之间以割点衔接DFS 生成树保证非树边两端是祖先-后代关系这是所有 $O(nm)$ 算法的共同前提边双连通分量可用先求桥再划分或直接类比强连通分量两种 Tarjan 求解也可用非树边覆盖 树上差分求解点双连通分量的 Tarjan 依赖割点性质割点会同时属于多个分量差分思想在两种分量上均可推广且在空间受限16MB 内存场景下仍是关键优化手段。若要继续深入可阅读仓库中相关的 图论概念、割点和桥、强连通分量 以及块-割点树Block-Cut Tree等进阶内容。【免费下载链接】OI-wiki:star2: Wiki of OI / ICPC for everyone. 某大型游戏线上攻略内含炫酷算术魔法项目地址: https://gitcode.com/GitHub_Trending/oi/OI-wiki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/12 3:49:42

风储VSG并网Simulink仿真:虚拟同步发电机惯量支撑全解析

风储VSG——基于虚拟同步发电机的风储并网系统Simulink仿真,这名字听着像又一个毕业论文选题。但真上手做起来,你会发现它解决的问题特别具体:风电场里一台台变流器越来越多,传统火电机组逐渐退出后,电网频率跌落时谁站…

2026/9/12 3:49:42

黎曼ζ函数:数学与物理的跨学科桥梁

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/12 4:29:47

PyMC 采样与推断方法实战指南:MCMC、变分推断与诊断调优

PyMC 采样与推断方法实战指南:MCMC、变分推断与诊断调优 【免费下载链接】scientific-agent-skills Turn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000 scientists worldwide. 165 ready-to-use validated skills…

2026/9/12 4:29:47

bd recall 命令深度指南:用 Beads 按 key 检索持久记忆

bd recall 命令深度指南&#xff1a;用 Beads 按 key 检索持久记忆 【免费下载链接】beads Beads - A memory upgrade for your coding agent 项目地址: https://gitcode.com/GitHub_Trending/beads1/beads 导读 bd recall <key> 是 Beads 持久记忆体系&#xff…

2026/9/12 4:29:46

ToF相机全链路解析:从光子到点云的硬件-驱动-应用协同

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/12 4:29:46

风光储并网系统Simulink建模与协同控制策略

1. 项目背景与核心价值风光储并网系统作为新能源电力领域的重要研究方向&#xff0c;其仿真建模对实际工程应用具有关键指导意义。这个Simulink模型研究项目聚焦永磁风机、光伏阵列与储能系统的协同运行机制&#xff0c;正是当前微电网和智能电网技术发展的前沿课题。在实际工程…

2026/9/12 2:05:33

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊&#xff01;#雷神 #复联”这类调侃式短标题&#xff0c;第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里&#xff0c;但细想一下就能发现&#xff0c;它真正碰到的根本不是…

2026/9/12 3:55:12

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊&#xff0c;可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”&#xff0c;你会发现&#xff0c;这场比较本质上是两个不同 IP 策略的长期结果对比&#xff1a;超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介&#xff1a;本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案&#xff0c;聚焦调制信号自动检测与识别这一典型无线通信任务&#xff0c;解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件&#xff08;10.73MB&#xff09;&…

2026/9/12 0:04:17

MATLAB仿生优化框架:长鼻浣熊算法多策略融合实现

简介&#xff1a;本资源是一份面向智能优化算法研究者与MATLAB初学者的仿生智能算法实践代码包&#xff0c;聚焦于长鼻浣熊优化算法&#xff08;COA&#xff09;的多策略改进与性能验证。针对传统COA易陷局部最优、收敛精度不足等问题&#xff0c;作者融合Circle映射初始化提升…

2026/9/12 0:04:17

【JAVA毕设源码分享】基于 JavaWeb 的校园一卡通管理系统的设计与实现 基于 JavaWeb 的校园卡业务管理系统(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

2026/9/12 0:04:17

【JAVA毕设源码分享】基于 Java 的图书馆借阅管理平台的搭建与实现 基于 Java 的图书馆综合管理系统(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

2026/9/10 12:32:02

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行&#xff0c;Type-C接口算是典型的“看着简单&#xff0c;做起来全坑”的东西。光引脚就24个&#xff0c;高低速信号、电源、控制线全部塞在一个小小的连接器里&#xff0c;如果PCB布局不做规划&#xff0c;打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/10 15:49:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码