Tarjan三把刀
搞过OI的对tarjan这个人大概都不陌生。这个人发明了很多神奇的算法,在OI届广被采用。
他最广泛采用的三个算法都是和$dfn$,$low$相关的。
有向图求强连通分量
其实说直白点,就是缩点。用得比较多的就是把一个有向有环图变为一个DAG。然后利用DAG的一些神奇的性质求解一些常见的问题。
核心代码如下:
void tarjan(int node){
vis[node]=1;dfn[node]=low[node]=++dfs_clock;
stack[++top]=node;
for(int i=LINK[node];i;i=e[i].next)
if(!dfn[e[i].y]){
tarjan(e[i].y);
low[node]=min(low[node],low[e[i].y]);
}else if(vis[e[i].y]) low[node]=min(low[node],dfn[e[i].y]);
if(dfn[node]==low[node]){
group_num++;
int tmp;
do{
tmp=stack[top--];
vis[tmp]=0;
group[tmp]=group_num;
}while(tmp!=node);
}
}
每次执行完后,$group$值相同的就是强连通分量里的。同时$group_{num}$也代表这里有几个强连通分量。
需要注意的是,这个只适用于有向图的缩点建图。如果以这个为模板特判父亲然后妄想把无向图缩成一棵树就是大错特错,至于有向图的缩点请参考下面的点双联通分量
无向图求割点/割边
求割点和割边其实无差,如果一个点存在一条边为割边,那么这个点一定是割点.
相应的,对于点$node$的儿子$son$,如果$dfn[node] \leq low[son] $,那么$edge_{(node,son)}$就是一条割边,而$node$也就是相应的割点。
特殊的,如果$node$为$root$,且只有一个儿子,也满足上述条件,但显然$node$不是割点,所有需要特判。
void tarjan(int node,int father){
vis[node]=1;dfn[node]=low[node]=++dfs_clock;
for(int i=LINK[node];i;i=e[i].next)if(e[i].y!=father){
if(!dfn[e[i].y]){
outdu[node]++;
tarjan(e[i].y,node);
cmin(low[node],low[e[i].y]);
if(low[e[i].y]>=dfn[node])OK[node]=1;
}else if(vis[e[i].y]) cmin(low[node],dfn[e[i].y]);
}
if(outdu[node]==1&&node==1)OK[node]=0;
}
无向图求点双联通分量
这一块相对来说比较复杂,可以去做一道题。什么是点双联通分量?直白点说就是从一个点到另一个点的一个"圈"。
之前说到了用求强连通分量的方法不能把一个图缩成树来搞,可以参考一下这张图。

如果只用那种方法,这整张图都会被缩成一个点,但是显然这个图应该是两个点双联通分量。
可以注意到,连接着两个点双联通分量的点是一个割点,因此可以借用之前思路来求解求解点双联通分量。
void tarjan(int node){
dfn[node]=low[node]=++dfs_clock;
stack[++top]=node;int son=0;
Auto(i,node)if(!dfn[e[i].y]){
tarjan(e[i].y);
cmin(low[node],low[e[i].y]);
if(low[e[i].y]>=dfn[node]){
int tmp;group_num++;
do{
tmp=stack[top--];
group[group_num].pb(tmp);
}while(tmp!=e[i].y);
group[group_num].pb(node);
}
}else cmin(low[node],dfn[e[i].y]);
}
乍一看好像和求强连通分量没什么区别,确实。这个只是把我们称之为缩点的过程移到了循环里。
在建树的时候也跟第一个的构建新图不是很一样。对于每个点双联通分量,新建一个点,这个点向点双联通分量里的每个点连边。最终一定能构成一棵树,而这个树上任意两点之间的路径上所包含的点(除虚点)都是在原图上必经的点。
Tarjan三把刀的更多相关文章
- 我的runloop学习笔记
前言:公司项目终于忙的差不多了,最近比较闲,想起叶大说过的iOS面试三把刀,GCD.runtime.runloop,runtime之前已经总结过了,GCD在另一篇博客里也做了一些小总结,今天准备把ru ...
- Solr学习(2) Solr4.2.0+IK Analyzer 2012
Solr学习(二) Solr4.2.0+IK Analyzer 2012 开场白: 本章简单讲述如何在solr中配置著名的 IK Analyzer 分词器. 本章建立在 Solr学习(一) 基础上进 ...
- 自学JQuery Mobile的几个例子
JQuery Mobile是一个用于构建移动Web应用程序的框架,适用于主流的移动设备(智能手机.平板电脑),该框架利用了HTML5和CSS3技术减少了额外的脚本文件的编写.具体JQuery Mobi ...
- android开发 RecyclerView 列表布局
创建一个一行的自定义布局 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns ...
- hihoCoder 1185 连通性·三(Tarjan缩点+暴力DFS)
#1185 : 连通性·三 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 暑假到了!!小Hi和小Ho为了体验生活,来到了住在大草原的约翰家.今天一大早,约翰因为有事要出 ...
- 《程序员代码面试指南》第三章 二叉树问题 Tarjan算法与并查集解决二叉树节点间最近公共祖先的批量查询问题
题目待续.... Tarjan算法与并查集解决二叉树节点间最近公共祖先的批量查询问题 java代码
- Tarjan在图论中的应用(三)——用Tarjan来求解2-SAT
前言 \(2-SAT\)的解法不止一种(例如暴搜?),但最高效的应该还是\(Tarjan\). 说来其实我早就写过用\(Tarjan\)求解\(2-SAT\)的题目了(就是这道题:[2019.8.14 ...
- Newnode's NOI(P?)模拟赛 第三题 (主席树优化建图 + tarjan)
题目/题解戳这里 这道题题目保证a,b,ca,b,ca,b,c各是一个排列-mdzz考场上想到正解但是没看到是排列,相等的情况想了半天-然后写了暴力60分走人- 由于两两间关系一定,那么就是一个竞赛图 ...
- OO第三单元——基于JML的社交网络总结
OO第三单元--基于JML的社交网络总结 一.JML知识梳理 1)JML的语言基础以及基本语法 JML是用于java程序进行规格化设计的一种表示语言,是一种行为接口规格语言.其为严格的程序设计提供了一 ...
随机推荐
- 代替Reflection(反射)的一些方法
Reflection(反射)是深入学习.Net必须掌握的技能之一.最初学Reflection的时候,的确是被惊住了,原来还可以这样.只要给你一个Assembly, 你就能获取到其中所有的类型,根据类型 ...
- 查看Linux版本信息
如何查看Linux系统使用的版本信息呢? 下面这篇文章收集.整理了一些常见的查看Linux系统版本的方法.由于手头只有Oracle Linux.Centos Linux.Redhat Linux三个版 ...
- 《java JDK7 学习笔记》之接口与多态
1.对于"定义行为"也就是接口,可以使用interface关键字定义,接口中的方法不能操作,直接标示为abstract,而且一定是public修饰的. 类要操作接口,必须使用imp ...
- Python进阶【第二篇】多线程、消息队列queue
1.Python多线程.多进程 目的提高并发 1.一个应用程序,可以有多进程和多线程 2.默认:单进程,单线程 3.单进程,多线程 IO操作,不占用CPU python的多线程:IO操作,多线程提供并 ...
- (转)String、StringBuffer与StringBuilder之间区别
原文地址: http://www.cnblogs.com/A_ming/archive/2010/04/13/1711395.html 关于这三个类在字符串处理中的位置不言而喻,那么他们到底有什么优缺 ...
- VB.NET中图像处理的一些技巧以及其和C#图像处理的差距。
早期的时候我使用的开发工具是VB6,VB6做图像处理的速度在我的软件Imageshop中有所体现,还是算可以的.目前,我已经改用C#来研究图像算法,C#中有指针,做图像处理起来效率确实要高不少.VB. ...
- sql 入门经典(第五版) Ryan Stephens 学习笔记 后续——存储引擎
一.引擎基础 1 查看系统支持的存储引擎 show engines; 2 查看表使用的存储引擎两种方法: a.show table status from database_name where na ...
- java程序设计线程池(newCachedThreadPool())
创建一个无界的可缓存的线程池,若线程长时间没用会自动销毁,直接上代码好了: import java.util.concurrent.ExecutorService; import java.util. ...
- linux 下 sudo 指令不需要输入密码的配置
sudo的配置文件位于 /etc/sudoers 里面.具体操作如下: 打开sudoers文件. bo@engineer ~/ $ sudo nano /etc/sudoers 假定你的用户名为 m ...
- rsyslog及logrotate小结
[root@node1 logrotate.d]# ls dracut haproxy httpd mcelog nginx ppp psacct syslog yum yum install n ...