【学习笔记】耳分解与无向图的双连通性

感觉之前对于这方面的理解还是不够深入。

1.1 1.1 1.1 在无向图 G = ( V , E ) G=(V,E) G=(V,E)中,有一个子图 G ′ = ( V ′ , E ′ ) G'=(V',E') G=(V,E)(不一定是导出子图,其实只看 V ′ V' V就好了),若简单路径或简单环 P : x 1 → x 2 → . . . → x k P:x_1\to x_2\to ...\to x_k P:x1x2...xk满足: x 1 , x k ∈ V ′ , x 2 , . . . , x k − 1 ∉ V ′ x_1,x_k\in V',x_2,...,x_{k-1}\notin V' x1,xkV,x2,...,xk1/V,则称 P P P G G G关于 G ′ G' G的耳。若 P P P是简单路径,则称 P P P G G G关于 G ′ G' G的开耳。

简单说明一下,这里不讨论自环,但是有重边。简单环必须满足经过的边也不同,经过的点只有起点和终点相同。

1.2 1.2 1.2 对于无向连通图 G G G,若连通图序列 ( G 0 , G 1 , . . . , G k ) (G_0,G_1,...,G_k) (G0,G1,...,Gk)满足:

  • G 0 G_0 G0是一个简单环(可以只有一个点), G k = G G_k=G Gk=G
  • G i − 1 G_{i-1} Gi1 G i G_{i} Gi的子图
  • G i = ( V i , E i ) G_i=(V_i,E_i) Gi=(Vi,Ei),则 E i \ E i − 1 E_i\backslash E_{i-1} Ei\Ei1构成 G i − 1 G_{i-1} Gi1的一个耳(开耳)

则称 ( G 0 , G 1 , . . . , G k ) (G_0,G_1,...,G_k) (G0,G1,...,Gk) G G G的一个耳分解(开耳分解)。

1.3 1.3 1.3 无向连通图 G G G存在耳分解当且仅当 G G G边双联通。

必要性:若 G G G有耳分解 ( G 0 , . . . , G k ) (G_0,...,G_k) (G0,...,Gk),则由于 G 0 G_0 G0是简单环,所以 G 0 G_0 G0是边双联通的。加入一个耳过后显然还是边双联通的,归纳即可。

充分性:考虑求出以 1 1 1为根的一颗DFS树,然后按照以下方法得到 G G G的一个耳分解:

  • 找到一条以 1 1 1为端点的非树边 1 → x 1\to x 1x,令 G 0 G_0 G0是由树上路径 1 → x 1\to x 1x加上这条非树边形成的简单环
  • 如果 G i G_i Gi的点集不为 V V V,找到一个不属于 G i G_i Gi的点 x x x满足 x x x的父亲 y y y属于 G i G_i Gi,则由边双连通性知存在一条返祖边,两端点分别是 y y y的祖先(包括自己) u u u x x x的后代 v v v,考虑树上路径 y → v y\to v yv并上边 ( u , v ) (u,v) (u,v),这是 G i G_i Gi的一个耳,令 G i + 1 G_{i+1} Gi+1 G i G_i Gi并上这个耳。
  • 直到 G i G_i Gi的点集为 V V V,这个时候如果还有 G G G中的边未加入,则可以每条边都作为一个耳依次加入。

因为始终保证 G i G_i Gi的点集是一个包含 1 1 1的树上连通块,所以总能找到这样的 x x x

1.4 1.4 1.4 至少存在 3 3 3个点的无向连通图存在开耳分解当且仅当 G G G点双联通。

1.5 1.5 1.5 (双极定向)给定无向图 G = ( V , E ) G=(V,E) G=(V,E)和不同的两个节点 s , t s,t s,t,则四个命题等价:

  1. 在添加无向边 ( s , t ) (s,t) (s,t)后, G G G点双联通
  2. G G G的圆方树中所有方点构成一条链, s → t s\to t st是圆方树的一条直径
  3. 存在一种对 G G G的边进行定向的方法,得到一个有向无环图,且 s s s入度为零, t t t出度为零,其余点出入度都不为零
  4. 存在一个所有点的排列 p 1 , p 2 , . . . , p n p_1,p_2,...,p_n p1,p2,...,pn,使得 p 1 = s , p n = t p_1=s,p_n=t p1=s,pn=t,且任意前缀和后缀的导出子图都是联通的。

证明:

首先1,2等价。考虑如果一个圆点 u u u连接了至少 3 3 3个方点,那么将 u u u删掉后会形成至少 3 3 3个连通块,显然只连一条 ( s , t ) (s,t) (s,t)是不行的。所以方点构成一条链,显然如果 s , t s,t s,t不是直径的两个端点那么 s , t s,t s,t之一还是会成为割点,否则显然就是点双了。

然后由3推4。容易发现取出一个拓扑排序即为满足条件的 p p p,因为正着来的时候加一个点显然仍然联通,所以任意前缀联通;反过来显然也可以说明任意后缀联通。

接下来由1推3。考虑取出 G G G的一个开耳分解,满足 G 0 G_0 G0包含边 ( s , t ) (s,t) (s,t),然后将 ( s , t ) (s,t) (s,t)之外的边都定向为从 s s s t t t。考虑每次加入一个开耳,若 G i G_i Gi的定向中 u u u可达 v v v,则令耳的方向为 u u u v v v,否则令耳的方向为 v v v u u u。(这个手段似乎在uoj的某道题中见过)。容易发现这样定向是满足条件的。

最后由4推1。假设命题1不成立,那么存在一个不同于 s , t s,t s,t的割点 u u u为割点,于是还有一个不包含 s , t s,t s,t的连通块,记作 S S S。设 S ∪ { u } S\cup \{u\} S{u}在排列 p p p中最早和最晚的点分别是 x , y x,y x,y,则 x , y x,y x,y中至少有一个不是 u u u。不妨设 x ≠ u x\ne u x=u(另一种情况取后缀即可),那么考虑前缀 p 1 , . . . , p i = x p_1,...,p_i=x p1,...,pi=x的导出子图,由于 u u u不在其中而 s , x s,x s,x都在其中,所以这张图一定不连通,矛盾。

白鹭兰

这题正常做应该能拿50,但是我是sb。

前面的部分不再赘述,需要关注的点是圆方树上的最优化问题往往可以贪心,即简单比较子树的大小,注意方点要看成0,圆点看成1。以及方点和圆点有时候都会对应大致相同的结论。这在 [省选联考 2023] 城市建造 中也有体现。

考虑构造,相当于有一个点双,固定了第一个加入的点 s s s和最后加入的点 t t t,满足每次加点后前缀和后缀的导出子图联通。

显然可以用双极定向的做法 O ( n 2 ) O(n^2) O(n2)构造。

更优秀的做法是考虑把DFS树取出来,然后每个子树只保留返祖边中深度最浅的那一条(也就是 l o w u low_u lowu),然后考虑缩二度点。 显然叶子一定是二度点,所以每次缩叶子即可。具体实现就是对每个点维护一个后继集合 L L L(有顺序),然后自底向上对于每个不再 s s s t t t路径上的 u u u,在 L f a u L_{fa_u} Lfau L l o w u L_{low_u} Llowu中插入 u u u,表示如果遍历到 f a u fa_u fau l o w u low_u lowu其中一个点则下一个遍历 u u u(注意二度点不会影响连通性)。这样就变成了直链的情形,直接遍历 s s s t t t的路径即可。

复杂度 O ( n + m ) O(n+m) O(n+m)

显然双极定向也能这么做。(?)

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define inf 0x3f3f3f3f
using namespace std;
const int N=5e5+5;
int n,m,dfn[N],low[N],fa[N],num,tot,sz[N];
vector<int>G[N];
stack<int>stk;
vector<int>G0[N];
vector<int>vec[N];
void add(int x,int y){G0[x].pb(y),G0[y].pb(x);//cout<<"add:"<<x<<" "<<y<<"\n";
}
void tarjan(int u){dfn[u]=low[u]=++num,stk.push(u);for(auto v:G[u]){if(!dfn[v]){tarjan(v),low[u]=min(low[u],low[v]);if(low[v]>=dfn[u]){tot++;int tmp=0;do{tmp=stk.top(),stk.pop();add(tmp,tot),vec[tot].pb(tmp);}while(tmp!=v);add(u,tot),vec[tot].pb(u);}}else low[u]=min(low[u],dfn[v]);}
}
int f[N],son[N],son0[N];
void calc(int v){int tmp=0;if(v<=n)tmp=sz[v]-sz[son[v]];else{for(auto e:G0[v]){if(e==son[v]||e==fa[v])continue;tmp=max(tmp,sz[e]);}}f[v]=max(f[son[v]],tmp);
}
int kmin=inf,rt;
void dfs(int u,int topf){sz[u]=(u<=n);for(auto v:G0[u]){if(v==topf)continue;fa[v]=u,dfs(v,u),sz[u]+=sz[v];if(sz[v]>sz[son[u]])son0[u]=son[u],son[u]=v;else if(sz[v]>sz[son0[u]])son0[u]=v;}calc(son[u]),calc(son0[u]);f[u]=f[son[u]];int tmp=0;if(u<=n)tmp=n-sz[son[u]]-sz[son0[u]];else{tmp=n-sz[u];for(auto v:G0[u]){if(v==son[u]||v==son0[u]||v==fa[u])continue;tmp=max(tmp,sz[v]);}}if((son0[u]||u==1)&&max(tmp,max(f[son[u]],f[son0[u]]))<kmin){kmin=max(tmp,max(f[son[u]],f[son0[u]]));rt=u;}
}
vector<vector<int>>opt;
vector<int>arr;
bool ban[N];
void dfs0(int u,int topf){if(u<=n)arr.pb(u);for(auto v:G0[u]){if(v==topf)continue;dfs0(v,u);}
}
vector<int>L[N];
int ban0[N],fa0[N],vs0[N],rk[N];
vector<int>G1[N],G2[N];
vector<pair<int,int>>edge0[N];
void tarjan0(int u){dfn[u]=low[u]=++num,rk[num]=u;for(auto v:G1[u]){if(!dfn[v]){fa0[v]=u,tarjan0(v),low[u]=min(low[u],low[v]);G2[u].pb(v);}else low[u]=min(low[u],dfn[v]);}
}
void dfs1(int u){for(auto v:G2[u]){dfs1(v);}if(!ban0[u]){L[fa0[u]].pb(u);L[rk[low[u]]].pb(u);}
}
void find(int u,int s,int t,int id){if(u!=s&&u!=t){arr.clear(),dfs0(u,id);opt.pb(arr);}vs0[u]=1;for(auto v:L[u]){if(!vs0[v])find(v,s,t,id);}
}
void sol(int u,int s,int t){//cout<<"sol:"<<u<<" "<<s<<" "<<t<<"\n";for(auto v:vec[u])dfn[v]=low[v]=ban0[v]=fa0[v]=vs0[v]=0,G1[v].clear(),G2[v].clear(),L[v].clear();num=0;for(auto e:edge0[u]){int x=e.fi,y=e.se;G1[x].pb(y),G1[y].pb(x);}tarjan0(s);vector<int>path;//for(auto v:vec[u])cout<<"find:"<<v<<" "<<fa0[v]<<"\n";//cout<<"path:"<<s<<" "<<t<<"\n";int x=t;while(x!=s)path.pb(x),ban0[x]=1,x=fa0[x];path.pb(x),ban0[x]=1;reverse(path.begin(),path.end());dfs1(s);for(auto e:path)find(e,s,t,u);
}
pair<int,int>edge[N];
int main(){//freopen("data.in","r",stdin);ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n>>m,tot=n;for(int i=1;i<=m;i++){int x,y;cin>>x>>y;G[x].pb(y),G[y].pb(x),edge[i]={x,y};}tarjan(1);dfs(1,0);for(int i=1;i<=m;i++){int x=edge[i].fi,y=edge[i].se;if(fa[fa[x]]==y)edge0[fa[x]].pb({x,y});else edge0[fa[y]].pb({x,y});}int x=rt;vector<int>vec0;if(son0[rt]){x=son0[rt];while(son[x])x=son[x];while(x!=rt)vec0.pb(x),ban[x]=1,x=fa[x];}while(x)vec0.pb(x),ban[x]=1,x=son[x];// for(auto e:vec0){//     cout<<e<<" ";// }for(int i=0;i<vec0.size();i++){int u=vec0[i];if(u<=n){arr.clear(),arr.pb(u);for(auto v:G0[u])if(!ban[v])dfs0(v,u);opt.pb(arr);}else{sol(u,vec0[i-1],vec0[i+1]);}}cout<<kmin<<" "<<opt.size()<<"\n";for(int i=0;i<opt.size();i++){cout<<opt[i].size()<<" ";for(auto e:opt[i])cout<<e<<" ";cout<<"\n";}//cout<<kmin<<" "<<opt.size()<<"\n";
}

总结

感觉之前做过的很多点双和边双的题都可以用这套理论来解释,但是能直观理解显然是更好的。

发现之前总是把点双简化成一个环,显然这在仙人掌图上是对的,但是对于复杂一点的题就不能直接这么做。

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

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

相关文章

R、Python的Copula变量相关性分析及AI大模型应用

在工程、水文和金融等各学科的研究中&#xff0c;总是会遇到很多变量&#xff0c;研究这些相互纠缠的变量间的相关关系是各学科的研究的重点。虽然皮尔逊相关、秩相关等相关系数提供了变量间相关关系的粗略结果&#xff0c;但这些系数都存在着无法克服的困难。例如&#xff0c;…

leetcode748-Shortest Completing Word

题目 给你一个字符串 licensePlate 和一个字符串数组 words &#xff0c;请你找出 words 中的 最短补全词 。 补全词 是一个包含 licensePlate 中所有字母的单词。忽略 licensePlate 中的 数字和空格 。不区分大小写。如果某个字母在 licensePlate 中出现不止一次&#xff0c…

MSSQL 命令行操作说明 sql server 2022 命令行下进行配置管理

说明&#xff1a;本文的内容是因为我在导入Access2019的 *.accdb 格式的数据时&#xff0c;总是出错的背景下&#xff0c;不得已搜索和整理了一下&#xff0c;如何用命令行进行sql server 数据库和用户管理的方法&#xff0c;作为从Access2019 直接导出数据到sql server 数据库…

linux限权

shell命令以及运行原理 什么是shell命令&#xff1a; 将使用者的命令翻译给核心&#xff08;kernel&#xff09;处理。同时&#xff0c;将核心的处理结果翻译给使用者。 shell就相当于操作系统外的一层外壳 其实就是登录linux时的一个可执行程序&#xff08;进程&#xff09…

滴滴春招深度揭秘:2024最全Spring Microservices Architecture面试题大全,每位开发者必备!99%的应聘者都强烈推荐!

在2024年&#xff0c;随着技术的不断演进和市场需求的快速变化&#xff0c;微服务架构已经成为现代软件开发的核心。滴滴作为领先的科技公司之一&#xff0c;特别重视在其庞大的交通网络服务平台中采用微服务架构&#xff0c;以提高系统的可扩展性、灵活性和维护性。微服务架构…

安装kali虚拟机

第一步&#xff1a;安装vmware 安装到最后&#xff0c;会提示输入序列号 安装成功后的图标 第二步&#xff1a;创建一个虚拟主机 创建了一个虚拟机 第三步&#xff1a;给虚拟机安装系统 安装系统&#xff0c;都必须有 系统镜像文件 一般系统镜像文件的后缀是 .iso 第一步&a…

利用CNN-Bigru-Attention模型输电线路故障诊断(Python代码,TensorFlow框架,)

效果视频&#xff1a;利用CNN-Bigru-Attention模型输电线路故障诊断(Python代码&#xff0c;TensorFlow框架&#xff0c;压缩包带有数据集和代码&#xff0c;解压缩可直接运行)_哔哩哔哩_bilibili 售后包免费远程协助运行&#xff08;用向日葵或者todesk软件协助&#xff09; …

Java中如何生成PDF文件的缩略图

在Java中生成PDF文件的缩略图可以使用Apache PDFBox库。以下是一个简单的示例代码来实现这个功能&#xff1a; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.tools.imageio.ImageIOUtil;import j…

主机连接本地VM虚拟机中的Ubuntu系统

虽然说本文特指Ubuntu系统&#xff0c;但过程原理也可以运用到类似的虚拟机上 前言 假设本机IPV4地址为10.26.231.99 1. 查看主机IP winr → 输入cmd → 点击确定&#xff0c;打开CMD → 输入ipconfig查看本地IPV4地址&#xff08;记下来&#xff09; 2. 虚拟机添加自动桥…

GoogleNet网络训练集和测试集搭建

测试集和训练集都是在之前搭建好的基础上进行修改的&#xff0c;重点记录与之前不同的代码。 还是使用的花分类的数据集进行训练和测试的。 一、训练集 1、搭建网络 设置参数&#xff1a;使用辅助分类器&#xff0c;采用权重初始化 net GoogleNet(num_classes5, aux_logi…

Midjourney常见玩法及prompt关键词技巧

今天系统给大家讲讲Midjourney的常见玩法和prompt关键词的一些注意事项&#xff0c;带大家入门&#xff5e;&#xff08;多图预警&#xff0c;建议收藏&#xff5e;&#xff09; 一、入门及常见玩法 1、注册并添加服务器&#xff08;会的童鞋可跳过&#xff5e;&#xff09; …

实在智能携AI Agent智能体亮相2024年度QCon全球软件开发大会

4月11日-13日&#xff0c;以“全面进化”作为2024年度主题的「QCon全球软件开发大会暨智能软件开发生态展」在北京隆重举行。作为AI准独角兽和超自动化头部企业&#xff0c;实在智能应邀出席发表《面向办公自动化领域的AI Agent建设思考与分享》演讲及圆桌交流&#xff0c;展示…

反转二叉树(力扣226)

解题思路&#xff1a;用队列进行前序遍历的同时把节点的左节点和右节点交换 具体代码如下&#xff1a; class Solution { public:TreeNode* invertTree(TreeNode* root) {if (root NULL) return root;swap(root->left, root->right); // 中invertTree(root->left)…

OpenHarmony南向开发案例【智慧中控面板(基于 Bearpi-Micro)】

1 开发环境搭建 【从0开始搭建开发环境】【快速搭建开发环境】 参考鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或复制转到。 【注意】&#xff1a;快速上手教程第六步出拉取代码时需要修改代码仓库地址 在MobaXterm中输入…

【模拟】Leetcode 提莫攻击

题目讲解 495. 提莫攻击 算法讲解 前后的两个数字之间的关系要么是相减之差 > 中毒时间 &#xff0c;要么反之 那即可通过示例&#xff0c;进行算法的模拟&#xff0c;得出上图的计算公式 class Solution { public:int findPoisonedDuration(vector<int>& time…

redis热key问题如何解决?

今天跟大家分享一个干货——redis热key问题 那么什么是redis热key呢&#xff1f; 在Redis中&#xff0c;热键问题是指那些经常被访问的键&#xff0c;它们会消耗大量的Redis服务器资源&#xff0c;当大量的请求集中在一个key上&#xff0c;会导致这个节点的cpu&#xff0c;内…

至少需要[XXXXMB]内存才能安装(宝塔导入数据库提示)

①我的2g内存腾讯云服务器想安装mysql8.0 ②宝塔提示“至少需要[3700MB]内存才能安装” 将数据库部署到宝塔上的时候提示-----》至少需要[XXXXMB]内存才能安装&#xff0c;解决的方法其实也很简单。 首先&#xff0c;进入文件夹/www/server/panel/class&#xff0c;找到找到…

使用Python插入100万条数据到MySQL数据库并将数据逐步写出到多个Excel

Python插入100万条数据到MySQL数据库 步骤一&#xff1a;导入所需模块和库 首先&#xff0c;我们需要导入 MySQL 连接器模块和 Faker 模块。MySQL 连接器模块用于连接到 MySQL 数据库&#xff0c;而 Faker 模块用于生成虚假数据。 import mysql.connector # 导入 MySQL 连接…

PostgreSQL的学习心得和知识总结(一百三十八)|深入理解PostgreSQL数据库之Protocol message构造和解析逻辑

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

【Java开发指南 | 第十一篇】Java运算符

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 算术运算符关系运算符位运算符逻辑运算符赋值运算符条件运算符&#xff08;?:&#xff09;instanceof 运算符Java运算符优先级 Java运算符包括&#xff1a;算术运算符、关系运算符、位运算符、逻辑运算符、赋值…