B3610 [图论与代数结构 801] 无向图的块 题解

2024-01-01 06:52

本文主要是介绍B3610 [图论与代数结构 801] 无向图的块 题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

B3610 [图论与代数结构 801] 无向图的块 题解

2023 2023 2023,再见。 2024 2024 2024,你好!

解法

其实就是统计点双连通分量的个数。需要注意的是,孤立点在这里不被看作块。本文使用 tarjan 算法来解决这道题。

概念明晰

  • 时间戳:这里记为 d f n i dfn_i dfni,表示第一次深度优先搜索到节点 i i i 的时间。时间 t i m e ∈ N + time \in \mathbb{N}^+ timeN+ 且随这搜索依次递增。
  • 搜索树:从选定的节点出发的深搜,每个节点仅搜索一次,把所有搜索路径组成一颗树,称为搜索树。如果给定的图不是一整个连通图,则称为搜索森林。
  • 追溯值:这里记为 l o w i low_i lowi,表示节点 i i i 最多经过一条返祖边能走到搜索树中以 i i i 的子树中的节点的最小 d f n dfn dfn 为多少(简洁的定义出自东灯的博客)。
  • 割点:对于一个无向图,如果把一个点删除后这个图的极大连通分量数增加了,那么这个点就是这个图的割点。

追溯值的计算

首先根据定义, l o w i low_i lowi 的初始值应赋为 d f n i dfn_i dfni。现在考虑怎么进一步更新 l o w i low_i lowi

  • 如果在搜索树上 i i i j j j 的父节点,则 l o w i = min ⁡ ( l o w i , l o w j ) low_i = \min(low_i,low_j) lowi=min(lowi,lowj)
  • 如果从 i i i j j j 的连边不是搜索树上的边,则 l o w i = min ⁡ ( l o w i , d f n j ) low_i= \min(low_i,dfn_j) lowi=min(lowi,dfnj)

割点的判定方法

定义搜索树中节点 i i i 的子树为 s o n i son_i soni

  • 如果 i i i 不是搜索树的根,则当 ∃ d f n i ≤ l o w j , j ∈ s o n i \exists dfn_i \le low_j,j \in son_i dfnilowj,jsoni 时, i i i 是割点。
  • 如果 i i i 是搜索树的根,则当有两个不同的 j j j 满足上述条件时, i i i 是割点。

判定方法的证明

如果 d f n i ≤ l o w j dfn_i \le low_j dfnilowj,则证明从 j ∈ s o n i j \in son_i jsoni 出发,不经过点 i i i 无论如何也不能到达 i i i 的祖先。所以如果把 i i i 删去,则 s o n i son_i soni 就会与原图分离。

代码

#include<bits/stdc++.h>
using namespace std;
#define Getchar() p1==p2 and (p2=(p1=Inf)+fread(Inf,1,1<<7,stdin),p1==p2)?EOF:*p1++
#define Putchar(c) p3==p4 and (fwrite(Ouf,1,1<<7,stdout),p3=Ouf),*p3++=c
char Inf[1<<7],Ouf[1<<7],*p1,*p2,*p3=Ouf,*p4=Ouf+(1<<7);
inline void read(int &x,char c=Getchar())
{bool f=c!='-';x=0;while(c<48 or c>57) c=Getchar(),f&=c!='-';while(c>=48 and c<=57) x=(x<<3)+(x<<1)+(c^48),c=Getchar();x=f?x:-x;
}
inline void write(int x)
{if(x<0) Putchar('-'),x=-x;if(x>=10) write(x/10),x%=10;Putchar(x^48);
}
struct my_stack
{int top,a[500010];inline int size(){return top;}inline int &operator[](const int &x){return a[x];}inline int back(){return a[top];}inline void push_back(const int &x){a[++top]=x;}inline void pop_back(){top--;}inline void clear(){top=0;}
};
my_stack v;
int n,m,head[500010],cnt,dfn[500010],low[500010],times,belong[500010],tot;
vector<int> ans[500010];
struct edge
{int to,next;
};
edge e[4000010];
inline void add(const int &x,const int &y) noexcept
{e[++cnt].to=y,e[cnt].next=head[x],head[x]=cnt;
}
inline void tarjan(const int &pos,const int &fa)
{int son=0;dfn[pos]=low[pos]=++times,v.push_back(pos);for(int i=head[pos];i;i=e[i].next)if(!dfn[e[i].to]){son++,tarjan(e[i].to,pos),low[pos]=min(low[pos],low[e[i].to]);if(low[e[i].to]>=dfn[pos]){tot++;while(v[v.top+1]!=e[i].to) ans[tot].push_back(v.back()),v.pop_back();ans[tot].push_back(pos);}}else if(e[i].to!=fa) low[pos]=min(low[pos],dfn[e[i].to]);
}
int main()
{read(n),read(m);for(int i=1,x,y;i<=m;i++) read(x),read(y),add(x,y),add(y,x);for(int i=1;i<=n;i++) if(!dfn[i]) v.clear(),tarjan(i,0);for(int i=1;i<=tot;i++) std::sort(ans[i].begin(),ans[i].end());std::sort(ans+1,ans+tot+1,[](std::vector<int> &lhs,std::vector<int> &rhs){for(int i=0;i<std::min(lhs.size(),rhs.size());i++) if(lhs[i]!=rhs[i]) return lhs[i]<rhs[i];return lhs.size()<rhs.size();});write(tot),Putchar('\n');for(int i=1;i<=tot;i++,Putchar('\n')){for(int j=0;j<ans[i].size();j++) write(ans[i][j]),Putchar(' ');}fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);return 0;
}

这篇关于B3610 [图论与代数结构 801] 无向图的块 题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/558454

相关文章

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

poj 2914 无向图的最小割

题意: 求无向图的最小割。 解析: 点击打开链接 代码: #pragma comment(linker, "/STACK:1677721600")#include <map>#include <set>#include <cmath>#include <queue>#include <stack>#include <vector>#include <cstd

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

C - Word Ladder题解

C - Word Ladder 题解 解题思路: 先输入两个字符串S 和t 然后在S和T中寻找有多少个字符不同的个数(也就是需要变换多少次) 开始替换时: tips: 字符串下标以0开始 我们定义两个变量a和b,用于记录当前遍历到的字符 首先是判断:如果这时a已经==b了,那么就跳过,不用管; 如果a大于b的话:那么我们就让s中的第i项替换成b,接着就直接输出S就行了。 这样

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使用了由[90]描述的第一个算法。开发者应该注意,由于数据点靠近包含的 Mat 元素的边界,返回的椭圆/旋转矩形数据

【秋招笔试】9.07米哈游秋招改编题-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收集 100+ 套笔试题,笔试真题 会在第一时间跟新 🍄 题面描述等均已改编,如果和你笔试题看到的题面描述

C语言程序设计(选择结构程序设计)

一、关系运算符和关系表达式 1.1关系运算符及其优先次序 ①<(小于) ②<=(小于或等于) ③>(大于) ④>=(大于或等于 ) ⑤==(等于) ⑥!=(不等于) 说明: 前4个优先级相同,后2个优先级相同,关系运算符的优先级低于算术运算符,关系运算符的优先级高于赋值运算符 1.2关系表达式 用关系运算符将两个表达式(可以是算术表达式或关系表达式,逻辑表达式,赋值表达式,字符

LeetCode 第414场周赛个人题解

目录 Q1. 将日期转换为二进制表示 原题链接 思路分析 AC代码 Q2. 范围内整数的最大得分 原题链接 思路分析 AC代码 Q3. 到达数组末尾的最大得分 原题链接 思路分析 AC代码 Q4. 吃掉所有兵需要的最多移动次数 原题链接 思路分析 AC代码 Q1. 将日期转换为二进制表示 原题链接 Q1. 将日期转换为二进制表示 思路分析