Tarjan-割点&桥&双连通
$Tarjan$求割点
感觉图论是个好神奇的东西啊,有各种奇奇怪怪的算法,而且非常巧妙。
周末之前说好回来之后进行一下学术交流,于是wzx就教了$Tarjan$,在这里我一定要说:
wzx AK IOI!
Tarjan发明了很多算法,而且还都叫一个名字,所以说只好用用途来区分它们。
闲聊时间结束。
首先,什么是割点呢?在一个无向图中,如果有一个顶点,删除这个顶点以及所有相关联的边以后,图的连通分量增多,就称这个点为割点。
首先找一个点作为根进行搜索,把图按照$dfs$的方法组织成一棵搜索树,树上的边一定都是图上的边,称为树边,而图上其余的边则为非树边(回边)。
如果一个点不能通过非树边而回到比他树上的父亲的$dfs$序更小的点,那么如果把它树上的父亲删掉,它就不能通过其他方法与图的其他部分联通,它的父亲就是一个割点。多么神奇啊!对于根节点,我们可以发现,如果它有不止一个的子树,那它就是割点了。看代码:
割点:https://www.luogu.org/problemnew/show/P3388
void dfs(int x,int roo,int Dad)
{
id[x]=low[x]=++cnt;
int j,cnts=;
for (R i=firs[x];i;i=g[i].nex)
{
j=g[i].too;
if(!id[j])
{
dfs(j,roo,x);
low[x]=min(low[x],low[j]);
if(x==roo) cnts++;
if(low[j]>=id[x]&&x!=roo) f[x]=;
}
else
{
if(j!=Dad) low[x]=min(low[x],low[j]);
}
}
if(x==roo&&cnts>=) f[x]=;
}
Tarjan求割点
这里有一句话还是比较重要的:
1 if(j!=Dad) low[x]=min(low[x],low[j]);
是防止重复走树边,其实也可以改成 $low[x]=min(low[x],id[j])$,这样更新出来的$low$可能不是真正的$low$,但是因为儿子到父亲的路径上不会再有别的点,所以这样也能保证正确性。
割点的理论知识似乎就到此为止了,现在还是看几道题比较好。
[POI2008]BLO-Blockade:https://www.luogu.org/problemnew/show/P3469
题意概述:在一个无向图中删去一个点后,还有多少个有序的(x,y)的点对本可以联通但是现在不能联通了?输出删除每一个点之后的答案。
这道题还是挺妙的,不算是板子题,有一点点思维含量,这样的题最适合入门新算法啦。
如果一个点不是割点,那么损失的点对只有与它直接相连的点;如果一个点是割点,那么割掉它之后图就分成了一些小块,在每个小块内仍是$size*(size-1)$;这么一说感觉也没啥思维难度啊...
# include <cstdio>
# include <iostream>
# define R register int using namespace std; const int maxn=;
const int maxm=;
int cnt=,h=,n,m,x,y,firs[maxn],f[maxn],id[maxn],low[maxn];
long long ans[maxn],siz[maxn];
struct edge
{
int too,nex;
}g[maxm<<]; void add(int x,int y)
{
g[++h].too=y;
g[h].nex=firs[x];
firs[x]=h;
} int read()
{
int x=;
char c=getchar();
while (!isdigit(c))
c=getchar();
while (isdigit(c))
{
x=(x<<)+(x<<)+(c^);
c=getchar();
}
return x;
} void dfs(int x,int roo)
{
id[x]=low[x]=++cnt;
int j,s=;
siz[x]=;
for (R i=firs[x];i;i=g[i].nex)
{
j=g[i].too;
if(!id[j])
{
dfs(j,roo);
low[x]=min(low[x],low[j]);
siz[x]+=siz[j];
if(low[j]>=id[x])
{
ans[x]+=(long long)s*siz[j];
s+=siz[j];
}
}
else
low[x]=min(low[x],id[j]);
}
ans[x]+=(long long)s*(n-s-);
} int main()
{
n=read();
m=read();
for (R i=;i<=m;++i)
{
x=read();
y=read();
add(x,y);
add(y,x);
}
for (R i=;i<=n;++i)
if(!id[i]) dfs(i,i);
for (R i=;i<=n;++i)
printf("%lld\n",(ans[i]+n-)<<);
return ;
}
BLO-blockade
其实割点的题比较少见唉...
$Tarjan$求割边(桥)
什么是割边呢?如果删去一条边后整个图变得不连通了,那么这条边就叫做这个图的一个割边。显然割边是对于无向图的一种东西,有向图是无法定义图的连通性的。
割点和割边听起来总是有种神奇的联系,所以有两个猜想:
1.两个割点中间连一条边一定是桥;
2.桥的两个端点都是割点;
然而并不是...都是错的...
如果对图上的边对于在搜索树上出现的位置进行分类,可以分为:树枝边(搜索树上的树枝),返祖边(指向搜索树上祖先的边),正向边(指向后代,但不是搜索树上的边),横叉边(指向搜索树与它不在同一子树上的边).然而无向图不存在横叉边.考虑反证法:如果$X$有指向其他子树的边,那必然也有从那边指过来的边,所以之前搜索时应该已经搜到过$X$了,这是不成立的.
那么一条边成为桥需要什么条件呢?首先对于搜索树上的一个节点$x$和它的子节点$y$,如果$low[y]>id[x]$,也就是说如果不走$(x,y)$这条边,$y$就无法与搜索树的其他部分相连通,那么$(x,y)$这条边就是一条割边.但是可以发现$low[y]<=id[y]$且$low[y]>id[x]$,而且$x$是$y$的直接祖先,所以可以认为是$low[y]=id[y]$,这就和缩点有一些相似之处了.这里有一些细节问题需要注意:怎样防止走反向边回到父亲去?一种最简单的方法是记录每个点在搜索书上的父亲.很可惜,这样做是错误的.如果一个点和它的父节点间有重边,那么这几条边肯定都不是割边,但是这样的做法却可能误判成割边.正确的做法是记录上一条走的边的编号且利用网络流反向边的标号技巧,注意不要走它即可。
来一道例题吧:
旅游航道:https://loj.ac/problem/10102
题意概述:求割边的数量。
# include <cstdio>
# include <iostream>
# include <cstring>
# define R register int using namespace std; const int maxn=;
int n,m,h,firs[maxn],low[maxn],id[maxn],cnt,x,y,ed;
struct edge
{
int too,nex;
}g[maxn<<]; void dfs (int x,int las)
{
int j;
low[x]=id[x]=++cnt;
for (R i=firs[x];i;i=g[i].nex)
{
j=g[i].too;
if(i==(las^)) continue;
if(!id[j]) dfs(j,i);
low[x]=min(low[x],low[j]);
if(low[j]>id[x]) ed++;
}
} void add (int x,int y)
{
g[++h].nex=firs[x];
firs[x]=h;
g[h].too=y;
} int main()
{
scanf("%d%d",&n,&m);
while(n||m)
{
h=;
cnt=ed=;
memset(firs,,sizeof(firs));
memset(id,,sizeof(id));
memset(low,,sizeof(low));
for (R i=;i<=m;++i)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for (R i=;i<=n;++i)
if(!id[i]) dfs(i,-);
printf("%d\n",ed);
scanf("%d%d",&n,&m);
}
return ;
}
旅游航道
割边好像不是很难。但是,难的还在后面。
边双连通分量
边双连通分量是什么?
---shzr
Tarjan-割点&桥&双连通的更多相关文章
- 图的割点 桥 双连通(byvoid)
[点连通度与边连通度] 在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合.一个图的点连通度的定义为,最小割点集 ...
- tarjan算法应用 割点 桥 双连通分量
tarjan算法的应用. 还需多练习--.遇上题目还是容易傻住 对于tarjan算法中使用到的Dfn和Low数组. low[u]:=min(low[u],dfn[v])--(u,v)为后向边,v不是u ...
- BZOJ BLO 1123 (割点)【双连通】
<题目链接> 以下内容转自李煜东的<算法竞赛进阶指南> 题目大意:现在给定一张连通的无向图,不包含重边.现在输出$n$个整数,表示将第$i$个节点的所有与其它节点相关联的边去掉 ...
- [题解](tarjan割点/点双)luogu_P3225_矿场搭建
首先和割点有关,求割点,然后这些割点应该把这个图分成了多个点双,可以考虑点双的缩点,假如缩点做的话我们要分析每个点双的性质和贡献 先拿出一个点双来,如果它没有连接着割点,那么至少要建两个,以防止其中一 ...
- 图论之tarjan真乃神人也,强连通分量,割点,桥,双连通他都会
先来%一下Robert Tarjan前辈 %%%%%%%%%%%%%%%%%% 然后是热情感谢下列并不止这些大佬的博客: 图连通性(一):Tarjan算法求解有向图强连通分量 图连通性(二):Tarj ...
- HDU4612(Warm up)2013多校2-图的边双连通问题(Tarjan算法+树形DP)
/** 题目大意: 给你一个无向连通图,问加上一条边后得到的图的最少的割边数; 算法思想: 图的边双连通Tarjan算法+树形DP; 即通过Tarjan算法对边双连通缩图,构成一棵树,然后用树形DP求 ...
- Tarjan算法——强连通、双连通、割点、桥
Tarjan算法 概念区分 有向图 强连通:在有向图\(G\)中,如果两个顶点\(u, v\ (u \neq v)\)间有一条从\(u\)到\(v\)的有向路径,同时还有一条从\(v\)到\(u\)的 ...
- Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)【转】【修改】
一.基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成 ...
- (转)Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)
基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个 ...
随机推荐
- jQuery自定义数组操作类(类似于List集合的增删改查)
js外部文件,前提需要引入jquery类库. 封装类代码如下: (function ($) { $.List = function () { var _list = new A ...
- (一)java并发知识图谱
- tomcat日志切割和定期删除(转载)
tomcat日志切割和定期删除 在tomcat的软件环境中,如果我们任由日志文件无限增长,总有一天会将磁盘占满的(废话).特别是在日志文件增长速度很快的一些情况下,按日志切割日志文件并删除,就是一件很 ...
- zsh: command not found cnpm,gulp等命令在zsh终端上报错的问题
以cnpm包为例,使用 npm install -g cnpm 创建了cnpm包.而在zsh终端上运行cnpm,出现 zsh: command not found: cnpm 的报错.同样的,gulp ...
- git中的ssh和https方式的使用(gitee为例)
在使用git管理代码,或者使用github,国内的码云(gitee)的时候,有两种方式可以使用,分别是https和ssh,以下均使用gitee为例. ssh方式 配置ssh,如果不配置ssh的话,cl ...
- js-权威指南学习笔记19.2
1.jQuery动画是异步的,会立刻返回,但动画会在后台执行,可传入函数作为动画完成的回调函数. 2.jQuery动画默认是队列化的. 3.stop()方法接受两个可选的布尔值参数,如果第一个参数是t ...
- 【代码笔记】iOS-动画的跳转
一,工程图. 二,代码. //点击任何处跳转到页面 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { CATransi ...
- iftop 命令
在Linux中有一个可以实施监控网络流量的一个工具那就是我们这次要说的iftop命令,这个命令不是系统自带的内置命令,在使用之前是需要先进行安装的 安装方式:yum -y install iftop就 ...
- Android 机器人项目Pepper_Android
github地址:https://github.com/ohwada/Pepper_Android
- Android长按事件和点击事件 冲突问题
长按点击的时候默认会触发点击事件,android系统是优先点击的,并且没有返回值:而长按事件是有返回值的,如果返回false,两个事件都会有响应,如果返回true则只响应长按事件.