圆方树学习笔记

news/2025/1/20 9:49:50/文章来源:https://www.cnblogs.com/zphh/p/18496256

元方树

下文除特殊强调外,所有图皆为无向图。

引入

  • 割点:在图中,删除某个点后,导致图不再连通的点。
  • 点双连通:在一张图中,取两个点 \(u\)\(v\),无论删去哪个点(除 \(u\)\(v\) 自身外),\(u\)\(v\) 都能连通,我们就说 \(u\)\(v\) 点双连通
  • 点双连通分量(后文称点双):对于一个无向图中的极大点双连通的子图,我们称这个子图为一个点双连通分量。

(from OI Wiki)

(下文中 \(u \leftrightarrow v\) 表示 \(u\)\(v\) 在同一个点双里,这只是我个人的写法,实在懒得写了)

但点双性质实在不优秀,\(a \leftrightarrow b, b \leftrightarrow c\) 并不能推出 \(a \leftrightarrow c\)

定义两条边相邻为:

  • 两条边有公共顶点。

定义两条边属于同一个点双:

  • 设两条边为 \((a, b)\)\((c, d)\),满足 \(\forall (x, y) \in \{a, b\} \times \{c, d\}, x \leftrightarrow y\) 就称 \((a, b)\)\((c, d)\) 属于同一个点双(即 \((a, b) \leftrightarrow (c, d)\))。

发现把边的定义整出来似乎就优秀了。

结论 \(1\):若 \((a, b) \leftrightarrow (c, d)\)\((c, d) \leftrightarrow (e, f)\),则 \((a, b) \leftrightarrow (e, f)\)

证明:

  • 根据定义,\((a, b) \leftrightarrow (e, f)\) 等价与 \(\forall (x, y) \in \{a, b\} \times \{e, f\}, x \leftrightarrow y\)
  • \((a, b) \leftrightarrow (c, d)\)\((e, f) \leftrightarrow (c, d)\)
  • 则任意的二元组 \((x, y)\)(这是二元组,不是边),一定满足 \(x\)\(y\) 都与 \(c\)\(d\) 两点属于同一个点双。
  • 我们从图中删掉 \(c\)\(d\) 中的任意一个都可以通过另外一个点从 \(x\) 到达 \(y\)
  • 如果删除的点不是 \(c\)\(d\) 也能到达(\(x \leftrightarrow c\)\(y \leftrightarrow c\)\(x \leftrightarrow d\)\(y \leftrightarrow d\), 不连通才怪……)。
  • \(a \leftrightarrow e\)\(a \leftrightarrow f\)\(b \leftrightarrow e\)\(b \leftrightarrow f\)
  • 得证。

(借用一下 OI Wiki 的图,侵删)

众所周知,一条返祖边(非树边)可以使原图多一个点双。

那什么时候才能使两个点双合并成一个点双呢?

考虑把所有边都对应一个点,如果产生了一个点双则将所有在原图中相邻的的边对应的点连起来(即上图蓝边)。

只要蓝边相邻就可合并为同一个点双。

结论 \(2\):对于树上的一个点双,深度最浅的点只有一个儿子。

证明:
使用反证法。

  • 若一个点双深度最浅的点有超过一个儿子。
  • 则其儿子中任意两个点均可以通过删掉父亲使其不连通,与定义不符。
  • 则一个点双深度最浅的点不会有超过一个儿子。
  • 得证。

正题

概念

圆方树是什么?

把一张图的所有点双统计出来,并对于每一个点双,新建一个方点(原图的点为圆点)。

将点双内所有圆点之间的边断开(点双外不断),并连到新建的方点上,如下图。

(好吧还是 OI Wiki 的)

实现

跑点双最好用的还是 tarjan 啊……

\(\text{dfn}_i\) 为点 \(i\) 的 dfs 序。

\(\text{low}_i\) 为点 \(i\) 能够通过非树边到达的 dfs 序最小的点的 dfs 序(包括它本身)。

结论 \(3\):根据 结论 \(2\),对于任意一条树边 \((u, v)\),满足 \(u \leftrightarrow v\)\(u\) 为点双最浅的点。

均满足 \(\text{low}_v = \text{dfn}_u\)(或 \(\text{low}_v \ge \text{dfn}_u\))。

证明:

  • 既然 \(u\)\(v\) 属于同一个点双,则其必有一条非树边连向上面。
  • \(v\) 点必能通过非树边走到 \(u\)
  • 它无法继续通过树边走到下面(结论 \(2\))。
  • 也无法通过另外一条非树边走到上面(若有非树边能通向上面,则 \(u\) 就不是点双最浅点了)。

后面基本就跟普通 tarjan 一样了,贴个代码(点双模板):

void tarjan(int u) {st.push(u);               // 把节点放到栈里dfn[u] = low[u] = ++tot;  // 更新两个值for (int v : g[u]) {if (!dfn[v]) {                     // 没访问过tarjan(v);                     // 访问low[u] = min(low[u], low[v]);  // 更新if (low[v] >= dfn[u]) {        // 改成 == 也行d_tot++;                   // 点双个数加 1d[d_tot].push_back(u);     // 更新点双while (st.top() != v) {    // 用栈死命弹int t = st.top();d[d_tot].push_back(t);st.pop();}d[d_tot].push_back(v);st.pop();}} elselow[u] = min(low[u], dfn[v]);  // 更新}if (g[u].size() == 0) {  // 特判单独一个点d_tot++;d[d_tot].push_back(u);}
}

建一颗圆方树也就简单了(前文有注释的就没写了):

void tarjan(int u) {dfn[u] = low[u] = ++tot;st.push(u);for (int v : g[u]) {if (!dfn[v]) {tarjan(v);chmin(low[u], low[v]);if (low[v] >= dfn[u]) {h_tot++;                // 建立方点h[h_tot].push_back(u);  // 无向图h[u].push_back(h_tot);while (st.top() != v) {int t = st.top();h[h_tot].push_back(t);h[t].push_back(h_tot);st.pop();}h[h_tot].push_back(v);h[v].push_back(h_tot);st.pop();}} else {chmin(low[u], dfn[v]);}}
}

一个技巧:区分圆点方点的最好方法就是把方点的下标从 \(n + 1\) 开始,即 h_tot 的初值赋为 \(n\)

胜利!!!

例题

洛谷 P4320 道路相遇

建一棵圆方树。

观察到 \(u\)\(v\) 的路径的必经点就是圆方树上 \(u\)\(v\) 的路径上的圆点个数。

题目就转换成了一个树上路径问题。

倍增 LCA 即可。

namespace zqh {
const int N = 1000005;int n, m, q, dfn[N], low[N], tot, h_tot, f[N][25], dep[N];
stack<int> st;
vector<int> g[N], h[N];void tarjan(int u) {  // 圆方树,注释前文有写dfn[u] = low[u] = ++tot;st.push(u);for (int v : g[u]) {if (!dfn[v]) {tarjan(v);chmin(low[u], low[v]);if (low[v] >= dfn[u]) {h_tot++;h[h_tot].push_back(u);h[u].push_back(h_tot);while (st.top() != v) {int t = st.top();h[h_tot].push_back(t);h[t].push_back(h_tot);st.pop();}h[h_tot].push_back(v);h[v].push_back(h_tot);st.pop();}} else {chmin(low[u], dfn[v]);}}
}void dfs(int u, int fa, int dp) {  // LCA,不用说啦吧f[u][0] = fa;dep[u] = dp;for (int x : h[u]) {if (x == fa)continue;dfs(x, u, dp + 1);}
}void build() {int t = log2(n);for (int i = 1; i <= t; i++) {for (int j = 1; j <= n; j++) {f[j][i] = f[f[j][i - 1]][i - 1];}}
}int lca(int x, int y) {if (dep[x] < dep[y])swap(x, y);int dep_max = 0;while ((1 << (dep_max)) <= dep[x]) {dep_max++;}for (int i = dep_max; i >= 0; i--) {if (dep[x] - (1 << i) >= dep[y]) {x = f[x][i];}}if (x == y)return x;for (int i = dep_max; i >= 0; i--) {if (f[x][i] != f[y][i]) {x = f[x][i];y = f[y][i];}}return f[x][0];
}int dis(int x, int y) {return dep[x] + dep[y] - 2 * dep[lca(x, y)];
}void init() {cin >> n >> m;h_tot = n;for (int i = 1; i <= m; i++) {int u, v;cin >> u >> v;g[u].push_back(v);g[v].push_back(u);}
}void solve() {for (int i = 1; i <= n; i++) {if (!dfn[i]) {tarjan(i);}}dfs(1, 0, 1);build();int q;cin >> q;while (q--) {int u, v;cin >> u >> v;cout << dis(u, v) / 2 + 1 << endl;  // 圆点个数}
}void main() {init();solve();
}
}  // namespace zqh

应该还会加的……

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

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

相关文章

WordPress产品导入后内容出现乱码,以及附属一些别的功能

效果图如下 该插件附带了一个可以把产品描述里面的超链接给去掉,以及有的产品图片点击会在地址栏上面显示图片的路径,在该插件可以进行关闭,并且替换成一个模态窗,还有对产品邮费展示进行了处理,到金额到达包邮的时候,别的邮费进行隐藏 下面是该插件源码目录结构duola …

如何利用甘特图进行高效管理?——附应用工具

通过快速创建甘特图并合理利用其进行项目管理,可以显著提升项目管理的效率和质量。同时,不断优化甘特图和项目管理流程也是实现持续改进的关键。甘特图通过横轴表示时间,纵轴表示任务,每个任务的开始和结束时间通过横向条形表示。甘特图可以帮助管理者对项目中的各个任务进…

互联网(internet)的基本组成

一般而言,互联网被认为由三个部分组成: Edge NetworkDevices and Endpoints: This part consists of all the devices at the periphery of the network that users directly interact with. It includes personal computers, laptops, smartphones, tablets, and various In…

WebRTC 笔记

目录通话建立流程特别提醒代码创建 RTCPeerConnection 对象获取本地摄像头/麦克风创建发起方会话描述对象(createOffer)连接的远程对等方属性(setRemoteDescription)建立一条最优的连接方式 WebRTC 允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(…

读量子霸权09电池

爱迪生、福特推动工业电气化,电池效率低是能源存储难题,量子计算机或助力电池设计与能源存储,锂空气电池、超级电池是候选技术,量子计算也应用于汽车设计。1. 能量 1.1. 爱迪生1.1.1. 爱迪生是工业和社会电气化背后不知疲倦的推动力1.1.2. 爱迪生青睐于电池1.2. 福特1.2.1.…

优化@Transactional事务性能(LazyConnectionDataSourceProxy)

背景 在项目开发中,使用 @Transactional 注解来管理事务非常方便,且优雅。但是也存在一个问题:长事务问题很多被 @Transactional 标记的方法,实际上并不需要进行数据库操作,或者说,它们在执行的很长一段时间内都不会真正触发数据库访问。举个例子,我们的业务逻辑可能如下…

ElasticSearch Java 使用

目录创建工程,导入坐标创建索引 index创建映射 mapping建立文档 document建立文档(通过 XContentBuilder)建立文档(使用 Jackson 转换实体)1)添加jackson坐标2)创建 Article 实体3)代码实现查询文档操作关键词查询字符串查询使用文档 ID 查询文档查询文档分页操作批量插…

IntelliJ IDEA 2024.3 Java开发工具

IntelliJ IDEA 2024.3 Java开发工具 JetBrains IntelliJ IDEA 2024 mac,是一款Java开发工具,IntelliJ IDEA 凭借无与伦比的 Java 和 Kotlin 支持脱颖而出。从一开始就支持尖IDEA 2024.3 中文版开发工具端语言功能,保持领先地位。IntelliJ IDEA 对您的代码了如指掌,利用这些…

AI应用实战课学习总结(6)分类算法分析实战

本文介绍了机器学习中的分类场景问题,常用的分类算法 以及 分类和回归的简单对比,最后通过一个医疗数据诊断分类的案例做了一次实战,相信对你理解分类应用应该有所帮助。大家好,我是Edison。 最近入坑黄佳老师的《AI应用实战课》,记录下我的学习之旅,也算是总结回顾。 今…

深入浅出索引(上)

1.什么是数据库索引,索引是干什么用的? 对于数据库的表而言,索引其实就是它的“目录”。 2.索引的三种实现方式?(暂时介绍3种) ①哈希表索引:哈希表是一种以键 - 值(key-value)存储数据的结构,我们只要输入待查找的值即 key,就可以找到其对应的值即 Value。哈希的思…

【推荐】一款开源且功能丰富、技术先进的在线项目任务管理系统

项目介绍 DooTask是一款基于PHP + Vue开源的、功能丰富、技术先进、安全性高的开源在线项目任务管理系统,适合各种规模的团队使用。提供各类文档协作工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM,文件管理等工具;同时消息功能使用非对称加密技术让你的沟通更…

《SpringBoot》自动装配原理(简单易懂)

引入 先看SpringBoot的主配置类 @SpringBootApplication public class DemoApplication{public static void main(String[] args){SpringApplication.run(StartEurekaApplication.class, args);} }@SpringBootApplication 点进@SpringBootApplication来看,发现@SpringBootAppl…