$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-割点&桥&双连通的更多相关文章

  1. 图的割点 桥 双连通(byvoid)

    [点连通度与边连通度] 在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合.一个图的点连通度的定义为,最小割点集 ...

  2. tarjan算法应用 割点 桥 双连通分量

    tarjan算法的应用. 还需多练习--.遇上题目还是容易傻住 对于tarjan算法中使用到的Dfn和Low数组. low[u]:=min(low[u],dfn[v])--(u,v)为后向边,v不是u ...

  3. BZOJ BLO 1123 (割点)【双连通】

    <题目链接> 以下内容转自李煜东的<算法竞赛进阶指南> 题目大意:现在给定一张连通的无向图,不包含重边.现在输出$n$个整数,表示将第$i$个节点的所有与其它节点相关联的边去掉 ...

  4. [题解](tarjan割点/点双)luogu_P3225_矿场搭建

    首先和割点有关,求割点,然后这些割点应该把这个图分成了多个点双,可以考虑点双的缩点,假如缩点做的话我们要分析每个点双的性质和贡献 先拿出一个点双来,如果它没有连接着割点,那么至少要建两个,以防止其中一 ...

  5. 图论之tarjan真乃神人也,强连通分量,割点,桥,双连通他都会

    先来%一下Robert Tarjan前辈 %%%%%%%%%%%%%%%%%% 然后是热情感谢下列并不止这些大佬的博客: 图连通性(一):Tarjan算法求解有向图强连通分量 图连通性(二):Tarj ...

  6. HDU4612(Warm up)2013多校2-图的边双连通问题(Tarjan算法+树形DP)

    /** 题目大意: 给你一个无向连通图,问加上一条边后得到的图的最少的割边数; 算法思想: 图的边双连通Tarjan算法+树形DP; 即通过Tarjan算法对边双连通缩图,构成一棵树,然后用树形DP求 ...

  7. Tarjan算法——强连通、双连通、割点、桥

    Tarjan算法 概念区分 有向图 强连通:在有向图\(G\)中,如果两个顶点\(u, v\ (u \neq v)\)间有一条从\(u\)到\(v\)的有向路径,同时还有一条从\(v\)到\(u\)的 ...

  8. Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)【转】【修改】

    一.基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成 ...

  9. (转)Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)

    基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个 ...

随机推荐

  1. [日常] Linux下vim的常用命令总结

    vim按d表示剪切按dd剪切一行vim命令:命令模式 /关键字 n继续向下查找 vim的多行注释:1.按ctrl + v进入 visual block模式2.按上下选中要注释的行3.按大写字母I,再插 ...

  2. Eclipse启动tomcat后404错误

    题描述 在eclipse部署web项目后,发现tomcat可以启动,但是访问http://localhost:8080地址报404错误.而不使用eclipse启动tomcat,直接通过通过tomcat ...

  3. 【Tomcat】部署Web到tomcat的四种方式

    一.静态部署 1.直接将web项目文件件拷贝到webapps 目录中     Tomcat的Webapps目录是Tomcat默认的应用目录,当服务器启动时,会加载所有这个目录下的应用.所以可以将JSP ...

  4. SQL 通配符的使用?三种

    SQL 通配符 通配符可用于替代字符串中的任何其他字符. SQL 通配符 在 SQL 中,通配符与 SQL LIKE 操作符一起使用. SQL 通配符用于搜索表中的数据. 在 SQL 中,可使用以下通 ...

  5. Android - Dagger2 使用和原理

    Dagger2从入门到放弃再到恍然大悟 http://www.jianshu.com/p/cd2c1c9f68d4 http://www.jianshu.com/p/39d1df6c877d http ...

  6. 在IDEA里创建web项目,以及web 项目部署

    在IDEA里创建web项目很简单: 1).先创建一个简单的maven项目,这个步骤我就不说了. 2).maven项目创建完后,因为我们需要的是web项目,因此我们要在pom.xml文件里设置packa ...

  7. JAVA高级面试总结-JVM篇

    1.Sun HotSpot VM,是JDK和Open JDK中自带的虚拟机,也是目前使用范围最广的Java虚拟机. 2.JVM内存分布 程序计数器:是一块较小的内存空间,可以看作是当前线程所执行的字节 ...

  8. 应用rbac组件 动态生成一级菜单

    动态生成一级菜单 改表结构 需要知道是否是菜单\icon\名称权限表 +字段: is_menu = models.BooleanField(max_length=32,verbose_name='是否 ...

  9. C#将图片存放到SQL SERVER数据库中的方法

    本文实例讲述了C#将图片存放到SQL SERVER数据库中的方法.分享给大家供大家参考.具体如下: 第一步: ? 1 2 3 4 5 6 7 8 9 10 //获取当前选择的图片 this.pictu ...

  10. apicloud 消息推送与接收

    待解决的问题,如下: 在使用apicloud 的时候我们,在开发用户登录的时候可能会遇到这样的问题,当有2个设备a,b同事使用的app的时候并且是同一个人登录,我们需要去做判断,即大家常说的单点登录. ...