Tarjan 全家桶

Tarjan 算法的原理是通过维护每个点的 dfs 序和最小回溯值 low 来判断图的性质。


割点

定义:删去后图的联通分量增加,即图的连通性被破坏的点。

按是否是根节点讨论。

非根节点的:若 \(low_u\ge dfn_u\),则该点为割点。子树内所有点无法回溯到该点以前的点,那么删去该点子树和原图分离,故该点为割点。

根节点:有两个及以上子节点。比较显然。

注意:孤立点不算作割点。

点击查看代码
    /*割点*/int iscut[N];inline void Wtarjan(int u, int fa){dfn[u] = low[u] = ++dt; int son = 0;for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(v == fa) continue;if(!dfn[v]){son++;Wtarjan(v, u);low[u] = min(low[u], low[v]);if(low[v] >= dfn[u] && fa) iscut[u] = 1;}else low[u] = min(dfn[v], low[u]);}if(!fa && son >= 2) iscut[u] = 1;}

点双联通分量

定义:删去任意一个点,不改变该分量连通性的极大连通子图。

更易懂的解释为:不含割点的极大图。

性质:

  • 两个点双至多存在一个公共点(该点一定是割点);

  • 任意一个非割点的点都只存在于一个点双中,割点一定属于两个及以上的点双;

  • 对于一个点双(除去孤立边),其内部任意两不同点一定存在两条及以上的点不重复的路径。

那么我们找到割点,也就找到了点双。

实现:实时维护一个栈,若 \(low_v\ge dfn_u\),则将栈内 \(v\) 及以上的点全部弹出作为一个点双,随后将 \(u\) 加入点双。

注意:孤立点算作点双。

点击查看代码
    /*点双*/int num, st[N], top;vector<int> ans[N];inline void Wtarjan(int u, int fa){dfn[u] = low[u] = ++dt; int son = 0; st[++top] = u;for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(v == fa) continue;if(!dfn[v]){son++;Wtatjan(v, u);low[u] = min(low[u], low[v]);if(low[v] >= dfn[u]){++num;while(top){ans[num].P_B(st[top--]);if(st[top + 1] == v) break;}ans[num].P_B(u);}}else low[u] = min(low[u], dfn[v]);}if(!fa && !son) ans[++num].P_B(u);}

割边(桥)

定义:删去后图的联通分量增加,即图的连通性被破坏的边。

实现:若一条边为 dfs 树上的边,且 \(low_v\gt dfn_u\),则该边为割边。子树上所有点都无法回溯至该点及以上的点,删去后子树与原图分离,故该边为割边。

注意:链式前向星存图若用 ii^1 来代表一条边的双向,下标需从 2 开始。求割边强制要求判父亲,及不能用 dfs 到该点的反向边更新回溯值。孤立边是割边。

点击查看代码
    /*割边*/// 边下标从 2 开始bool isbridge[N << 1];inline void Wtarjan(int u, int fa){dfn[u] = low[u] = ++dt;for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(v == fa) continue; // necessaryif(!dfn[v]){Wtarjan(v, u);low[u] = min(low[u], low[v]);if(low[v] > dfn[u]) isbridge[i] = isbridge[i ^ 1] = 1;}else low[u] = min(low[u], dfn[v]);}}

边双连通分量

定义:删去任意一条边,不改变该分量连通性的极大连通子图。即不存在割边的极大联通子图。

实现:先求割边,将割边删去后,每个连通块为一个边双。

点击查看代码
    /*边双*/// 先求割边,将割边删去,剩下的即为边双bool yz[N];vector<int> ans[N];inline void Wtarjan(int u, int now){yz[u] = 1; ans[now].P_B(u);for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(yz[v] || isbridge[i]) continue;Wtarjan(v, now);}}

强连通分量

更常见的形式为缩点。主要存在于有向图。

定义:任意两点相互可达的极大连通子图。

实现:维护一个栈,若一个点遍历完子树后仍有 \(low_u=dfn_u\),则栈内 \(u\) 及以上的点构成一个强联通分量,可缩为一个点。详细原理见此。

点击查看代码
    /*强联通分量(缩点)*/int num, bl[N], st[N], top;inline void Wtarjan(int u, int fa){dfn[u] = low[u] = ++dt; st[++top] = u;for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(v == fa) continue;if(!dfn[v]){Wtarjan(v, u);low[u] = min(low[u], low[v]);}else if(!bl[v]) low[u] = min(low[u], dfn[v]);}if(low[u] == dfn[u]){++num;while(st[top] != u) bl[st[top--]] = num;top--, bl[u] = num;}}

完整实现

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
#define P_B(x) push_back(x)namespace Wisadel
{ // Tarjan 全家桶 int hh[N], to[N << 1], ne[N << 1], cnt;int dfn[N], dt, low[N];inline void Wadd(int u, int v){to[++cnt] = v; ne[cnt] = hh[u]; hh[u] = cnt;}/*割点*/int iscut[N];inline void Wtarjan(int u, int fa){dfn[u] = low[u] = ++dt; int son = 0;for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(v == fa) continue;if(!dfn[v]){son++;Wtarjan(v, u);low[u] = min(low[u], low[v]);if(low[v] >= dfn[u] && fa) iscut[u] = 1;}else low[u] = min(dfn[v], low[u]);}if(!fa && son >= 2) iscut[u] = 1;}/*点双*/int num, st[N], top;vector<int> ans[N];inline void Wtarjan(int u, int fa){dfn[u] = low[u] = ++dt; int son = 0; st[++top] = u;for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(v == fa) continue;if(!dfn[v]){son++;Wtatjan(v, u);low[u] = min(low[u], low[v]);if(low[v] >= dfn[u]){++num;while(top){ans[num].P_B(st[top--]);if(st[top + 1] == v) break;}ans[num].P_B(u);}}else low[u] = min(low[u], dfn[v]);}if(!fa && !son) ans[++num].P_B(u);}/*割边*/// 边下标从 2 开始bool isbridge[N << 1];inline void Wtarjan(int u, int fa){dfn[u] = low[u] = ++dt;for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(v == fa) continue; // necessaryif(!dfn[v]){Wtarjan(v, u);low[u] = min(low[u], low[v]);if(low[v] > dfn[u]) isbridge[i] = isbridge[i ^ 1] = 1;}else low[u] = min(low[u], dfn[v]);}}/*边双*/// 先求割边,将割边删去,剩下的即为边双bool yz[N];vector<int> ans[N];inline void Wtarjan(int u, int now){yz[u] = 1; ans[now].P_B(u);for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(yz[v] || isbridge[i]) continue;Wtarjan(v, now);}}/*强联通分量(缩点)*/int num, bl[N], st[N], top;inline void Wtarjan(int u, int fa){dfn[u] = low[u] = ++dt; st[++top] = u;for(int i = hh[u]; i != -1; i = ne[i]){int v = to[i];if(v == fa) continue;if(!dfn[v]){Wtarjan(v, u);low[u] = min(low[u], low[v]);}else if(!bl[v]) low[u] = min(low[u], dfn[v]);}if(low[u] == dfn[u]){++num;while(st[top] != u) bl[st[top--]] = num;top--, bl[u] = num;}}
}
signed main(){}

未经运行检测,如果哪写挂了记得告诉我啊(


祝大家 NOIP rp++!

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

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

相关文章

2024年各编程语言运行100万个并发任务需要多少内存?

你还记得2023年那篇比较各种流行编程语言异步编程内存消耗比较的文章吗? 现在是2024年底,我很好奇在一年时间里,随着各种语言的最新版本发布,情况有什么变化。 让我们再次进行基准测试,看看结果! 基准 用于基准测试的程序与去年相同:让我们启动 N 个并发任务,每个任务等…

用星球助手导出帖子的手把手教程

当我们把星球的帖子下载到本地电脑之后, 如果想要导出成PDF或者Word之类的格式进行学习, 该怎么弄呢? 其实也是相当简单的. 到"搜索"模块里, 选择"帖子", 输入关键词后者留空都可以, 点击"搜索", 在出现的帖子的右上角有三个图标, 从左到右分别…

OpenVZ 8.0 - 基于容器的 Linux 开源虚拟化解决方案

OpenVZ 8.0 - 基于容器的 Linux 开源虚拟化解决方案OpenVZ 8.0 - 基于容器的 Linux 开源虚拟化解决方案 Open source container-based virtualization for Linux 请访问原文链接:https://sysin.org/blog/openvz-8/ 查看最新版。原创作品,转载请保留出处。 作者主页:sysin.or…

Virtuozzo Hybrid Server 8.0 - 容器、计算和存储虚拟化平台

Virtuozzo Hybrid Server 8.0 - 容器、计算和存储虚拟化平台Virtuozzo Hybrid Server 8.0 - 容器、计算和存储虚拟化平台 The VMware alternative for service providers and enterprises 请访问原文链接:https://sysin.org/blog/virtuozzo-hybrid-server-8/ 查看最新版。原创…

一款开源、免费、美观的 Avalonia UI 原生控件库 - Semi Avalonia

前言 最近发现DotNetGuide技术社区交流群有不少小伙伴在学习Avalonia,今天大姚给大家分享一款开源、免费、美观的 Avalonia UI 原生控件库:Semi Avalonia。Avalonia项目介绍 Avalonia是一个强大的框架,使开发人员能够使用.NET创建跨平台应用程序。它使用自己的渲染引擎绘制U…

用星球助手搜索本地帖子的手把手教程

通过知识星球手机App或者网页的人都知道, 搜点东西是真费劲, 要么没法搜到想要的东西, 要么就是搜出来一大堆有用的没用的, 根本找不到自己真正想要的东西. 好在星球助手极大程度解决了这个问题. 我们将帖子下载到本地电脑上之后, 我们可以用更加精准的关键词进行搜索, 针对问答…

读数据质量管理:数据可靠性与数据质量问题解决之道18数据发现

数据发现1. 让元数据为业务服务 1.1. 在过去十多年中,数据团队越来越擅长收集大量的数据 1.2. 公司如今正在收集越来越多关于其数据的数据,也就是元数据1.2.1. dbt等ETL解决方案让跟踪和使用元数据变得容易,而云服务提供商则使栈中数据解决方案之间的元数据的互操作性变得更…

初见客户交流指南:精准高效,建立信任

在已知客户对产品有大致需求(询盘)的背景下,初次拜访交流至关重要。以下是一份实用的交流指南: 充分准备 自我介绍清晰明了 简洁明了地告知客户您的基本信息,包括姓名、籍贯、公司名称、职位以及您在决策过程中的角色。 树立专业形象,传达出“业务方面,找我就对了”的自…

《数字经济产业链》及其相关公司 —— 深度梳理

《数字经济产业链》及其相关公司 —— 深度梳理 来源:行业研究报告发布时间:2023 年 05 月 22 日 11:07:35(网经社讯)产业链分析 1、产业链概述 2、数字经济行业上游: (1)5G 根据中国信通院、中国移动、中国电信、中国联通、工信部,综合市场公开资料,2025 年中国 5G …

写一个方法检测页面中的所有标签是否正确闭合

function checkTagClosure(htmlString) {// 使用栈来跟踪打开的标签const tagStack = [];// 正则表达式匹配标签 (包括自闭合标签)const tagRegex = /<\/?([a-zA-Z][a-zA-Z0-9]*)\s*\/?>/g;let match;while ((match = tagRegex.exec(htmlString)) !== null) {const tag…

【api开发】API通信协议总结

API协议 1. REST(Representational State Transfer) 一种用于设计网络应用程序的架构风格。它强调无状态通信,使用标准的HTTP方法(GET、POST、PUT、DELETE),并通过URL识别资源。 2. GraphQL 一种API查询语言,允许客户端精确请求它们所需的数据,不多也不少。这种效率是G…

你有使用过picture标签吗?说说它有哪些运用场景

是的,我了解 <picture> 元素。它是一个 HTML5 元素,用于为不同的屏幕尺寸、设备像素比或文件格式提供不同的图像版本。浏览器会根据当前环境选择最合适的图像显示,从而优化页面加载速度和用户体验。 <picture> 元素本身并不显示图像,而是充当一个容器,其中包含…