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

相关文章

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

结构体和联合体的区别及说明

《结构体和联合体的区别及说明》文章主要介绍了C语言中的结构体和联合体,结构体是一种自定义的复合数据类型,可以包含多个成员,每个成员可以是不同的数据类型,联合体是一种特殊的数据结构,可以在内存中共享同一... 目录结构体和联合体的区别1. 结构体(Struct)2. 联合体(Union)3. 联合体与结构体的

PostgreSQL如何查询表结构和索引信息

《PostgreSQL如何查询表结构和索引信息》文章介绍了在PostgreSQL中查询表结构和索引信息的几种方法,包括使用`d`元命令、系统数据字典查询以及使用可视化工具DBeaver... 目录前言使用\d元命令查看表字段信息和索引信息通过系统数据字典查询表结构通过系统数据字典查询索引信息查询所有的表名可

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 元素的边界,返回的椭圆/旋转矩形数据