概述

在一个无向图中,若任意两点间至少存在两条“点不重复”的路径,则说这个图是点双连通的(简称双连通,biconnected)

在一个无向图中,点双连通的极大子图称为点双连通分量(简称双连通分量,Biconnected Component,BCC)

性质

  1. 任意两点间至少存在两条点不重复的路径等价于图中删去任意一个点都不会改变图的连通性,即BCC中无割点
  2. 若BCC间有公共点,则公共点为原图的割点
  3. 无向连通图中割点一定属于至少两个BCC,非割点只属于一个BCC

算法

在Tarjan过程中维护一个栈,每次Tarjan到一个结点就将该结点入栈,回溯时若目标结点low值不小于当前结点dfn值就出栈直到目标结点(目标结点也出栈),将出栈结点和当前结点存入BCC

(说实话我觉得存点不比存边难理解和实现啊……下面会解释)

理解

首先申明一下,在我找到的BCC资料中,在算法实现中均将两个点和一条边构成的图称为BCC,此文章也沿用此的规定

如下图:

我猜想可能是因为割点的定义,此图中两个点均不为割点,所以此图也属于BCC?

总之做题时注意题面要求,若要求的不含此种BCC则判断每个BCC的大小即可

无向连通图中割点一定属于至少两个BCC,非割点只属于一个BCC

有了上面的规定我们也不难理解这一条了:割点就算相邻也会属于至少两个BCC;BCC间的交点都是割点,所以非割点只属于一个BCC

到一个结点就将该结点入栈

为什么用栈存储呢?因为DFS是由上到下的,而分离BCC是自下而上的,需要后进先出的数据结构——栈

回溯时若目标结点low值不小于当前结点dfn值就出栈直到目标结点(目标结点也出栈),将出栈结点和当前结点存入BCC

对于每个BCC,它在DFS树中最先被发现的点一定是割点或DFS树的树根

证明:割点是BCC间的交点,故割点在BCC的边缘,且BCC间通过割点连接,所以BCC在DFS树中最先被发现的点是割点;特殊情况是对于开始DFS的点属于的BCC,其最先被发现的点就是DFS树的树根

上面的结论等价于每个BCC都在其最先被发现的点(一个割点或树根)的子树中

这样每发现一个BCC(low[v]>=dfn[u]),就将该子树出栈,并将该子树和当前结点(割点或树根)加入BCC中。上面的操作与此描述等价

(我就是因为这个条件“将子树出栈”没理解写错了结果调了一晚上poj2942)

综上,存点是不是很好理解?存边虽然不会涉及重复问题(割点属于至少两个BCC),但会有很多无用操作。个人觉得存点也是个不错的选择。

模板

并没有模板题

 #include<cstdio>
#include<cctype>
#include<vector>
using namespace std;
struct edge
{
int to,pre;
}edges[];
int head[],dfn[],dfs_clock,tot;
int num;//BCC数量
int stack[],top;//栈
vector<int>bcc[];
int tarjan(int u,int fa)
{
int lowu=dfn[u]=++dfs_clock;
for(int i=head[u];i;i=edges[i].pre)
if(!dfn[edges[i].to])
{
stack[++top]=edges[i].to;//搜索到的点入栈
int lowv=tarjan(edges[i].to,u);
lowu=min(lowu,lowv);
if(lowv>=dfn[u])//是割点或根
{
num++;
while(stack[top]!=edges[i].to)//将点出栈直到目标点
bcc[num].push_back(stack[top--]);
bcc[num].push_back(stack[top--]);//目标点出栈
bcc[num].push_back(u);//不要忘了将当前点存入bcc
}
}
else if(edges[i].to!=fa)
lowu=min(lowu,dfn[edges[i].to]);
return lowu;
}
void add(int x,int y)//邻接表存边
{
edges[++tot].to=y;
edges[tot].pre=head[x];
head[x]=tot;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
for(int i=;i<=n;i++)//遍历n个点tarjan
if(!dfn[i])
{
stack[top=]=i;
tarjan(i,i);
}
for(int i=;i<=num;i++)
{
printf("BCC#%d: ",i);
for(int j=;j<bcc[i].size();j++)
printf("%d ",bcc[i][j]);
printf("\n");
}
return ;
}

双连通分量

Tarjan求点双连通分量的更多相关文章

  1. hdu 2460(tarjan求边双连通分量+LCA)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2460 思路:题目的意思是要求在原图中加边后桥的数量,首先我们可以通过Tarjan求边双连通分量,对于边 ...

  2. [Codeforces 555E]Case of Computer Network(Tarjan求边-双连通分量+树上差分)

    [Codeforces 555E]Case of Computer Network(Tarjan求边-双连通分量+树上差分) 题面 给出一个无向图,以及q条有向路径.问是否存在一种给边定向的方案,使得 ...

  3. C++[Tarjan求点双连通分量,割点][HNOI2012]矿场搭建

    最近在学图论相关的内容,阅读这篇博客的前提是你已经基本了解了Tarjan求点双. 由割点的定义(删去这个点就可使这个图不连通)我们可以知道,坍塌的挖煤点只有在割点上才会使这个图不连通,而除了割点的其他 ...

  4. tarjan算法求桥双连通分量 POJ 3177 Redundant Paths

    POJ 3177 Redundant Paths Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 12598   Accept ...

  5. [HNOI2012]矿场搭建(tarjan求点双)

    题目 Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无 ...

  6. tarjan复习笔记 双连通分量,强连通分量

    声明:图自行参考割点和桥QVQ 双连通分量 如果一个无向连通图\(G=(V,E)\)中不存在割点(相对于这个图),则称它为点双连通图 如果一个无向连通图\(G=(V,E)\)中不存在割边(相对于这个图 ...

  7. 6409. 【NOIP2019模拟11.06】困难的图论(Tarjan求点双)

    题目描述 Description 给定由 n 个点 m 条边组成的无向连通图,保证没有重边和自环. 你需要找出所有边,满足这些边恰好存在于一个简单环中.一个环被称为简单环,当且仅当它包含的所有点都只在 ...

  8. 洛谷P2860 [USACO06JAN]冗余路径Redundant Paths(tarjan求边双联通分量)

    题目描述 In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1. ...

  9. POJ 2942 Knights of the Round Table 补图+tarjan求点双联通分量+二分图染色+debug

    题面还好,就不描述了 重点说题解: 由于仇恨关系不好处理,所以可以搞补图存不仇恨关系, 如果一个桌子上面的人能坐到一起,显然他们满足能构成一个环 所以跑点双联通分量 求点双联通分量我用的是向栈中pus ...

随机推荐

  1. Autofac依赖注入容器

    依赖注入容器-- Autofac https://github.com/danielpalme/IocPerformance Unity 更新频率高,微软的项目Grace 综合性能更高 目录: 一.简 ...

  2. 谈一谈 Normalize.css

    Normalize.css是一种CSS reset的替代方案.它在默认的HTML元素样式上提供了跨浏览器的高度一致性.相比于传统的CSS reset,Normalize.css是一种现代的.为HTML ...

  3. Azure Blob 存储简介

    Azure Blob 存储是 Microsoft 提供的适用于云的对象存储解决方案. Blob 存储最适合存储巨量的非结构化数据. 非结构化数据是不遵循特定数据模型或定义(如文本或二进制数据)的数据. ...

  4. 关于js查找和筛选和循环的几种方式

    find(); find() 方法返回通过测试(函数内判断)的数组的第一个元素的值. find() 方法为数组中的每个元素都调用一次函数执行: 当数组中的元素在测试条件时返回 true 时, find ...

  5. sql server 查询某段日期某段时间内的数据

    如我要取8月1号到8月30号之间的早上7点半到晚上八点半这段时间内的数据. SELECT * FROM tableName AS tWHERE t.create_date BETWEEN '2017- ...

  6. Oracle 用户管理权限

    Oracle 用户管理权限 一.创建用户的Profile文件 SQL> create profile student limit // student为资源文件名 FAILED_LOGIN_AT ...

  7. CF 403D Beautiful Pairs of Numbers

    The sequence of integer pairs (a1, b1), (a2, b2), ..., (ak, bk) is beautiful, if the following state ...

  8. neo4j allshortestpaths查询路径不准确问题

    同样是5年开发,年薪50万和年薪15万的差距在哪里-.>>> 基本语法 使用neo4j cypher查询语言的小伙伴都知道cypher提供了两个查询最短路径的特殊函数shortest ...

  9. JS基础_强制类型转换-String

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  10. performance面板使用,以及解决动画卡顿

    https://googlechrome.github.io/devtools-samples/jank//    官方案例 https://juejin.im/post/5b65105f518825 ...