重修 Tarjan
Tarjan是谁
Tarjan's SCCs(有向图强连通分量)algorithm
给定⼀个有向图 \(G\),若存在 \(rt\in V\),满⾜从 \(rt\) 出发能到达 \(V\) 中的所有的点,则称 \(G\) 是⼀个源点为 \(rt\) 流图。
从 \(rt\) 出发做 \(DFS\)。
符号表
\(fa[x]\):\(x\) 节点的父亲节点
\(anc[x]\):\(x\) 节点的祖先点集
\(son[x]\):搜索树中 \(x\) 节点的儿子节点集
\(e[x]\):\(\{y \ | \ (x \to y)\in E\}\)
\(dfn[x]\):\(x\) 节点的时间戳
\(sbt[x]\):\(x\) 节点为根的子树点集
\(low[x]\):\(x\) 节点的追溯值
\(G\) 中的每条有向边 \(x\to y\) 必然是以下四种之⼀:
枝:\(x=fa[y]\)
前:\(x\in (anc[y]-fa[y])\)
后:\(y\in (anc[x]-fa[x])\)
横:\(x\notin anc[y] \ \and \ y\notin anc[x]\) 此时一定满足 \(dfn[x]>dfn[y]\)
节点上的数字为时间戳:

分析
我们在搜索树上分析,发现“前”边没有什么用处,因为搜索树上本来就存在从 \(x\) 到 \(y\) 的路径。“后”边非常有用,它可以和搜索树上从 \(x\) 到 \(y\) 的路径⼀起构成环。“横”边要看情况,如果从 \(y\) 出发能找到⼀条路径回到 \(x\) 的祖先节点,那么 \(x\to y\) 就是有用的。
\(low[x]\) 定义为满⾜以下条件的节点的最小时间戳:
该点在栈中。
存在⼀条从 \(sbt[x]\) 出发的有向边,以该点为终点。
\(tarjan(x)\) 主体
\(low[x]=dfn[x]=++dfn\_time\)
\(for(y:e[x])\begin{cases}tarjan(y) \ check\_min(low[x],\color{blue}{low[y]}) & !vis[y] \\ check\_min(low[x],\color{red}{dfn[y]}) & y\in anc[x] \\ do \ nothing & otherwise\end{cases}\)
\(if(low[x]=dfn[x]) \ pop \ stack \ until \ x \ is \ popped \to a \ SCC\)
注意标红和标蓝的不能改,不能错!!!
Code
vector<int> e[N];
int dfn[N];//时间戳
int low[N];//追溯值
int tim=0;//时间戳计数器
int col[N];//所属 SCC (为其中一个点的 id)
int st[N];//stack
int tot=0;//stack_top
bool in[N];//是(1)否(0)在栈中
void tar(int x){
dfn[x]=low[x]=++tim;//init
st[++tot]=x;//进栈
in[x]=1;
for(int i:e[x]){
if(!dfn[i]){//萌新
tar(i);//递归
ckmn(low[x],low[i]);
}else if(in[i]){//祖先
ckmn(low[x],dfn[i]);
}
}
if(low[x]==dfn[x]){//导出 SCC
do{
col[st[tot]]=x;
//着上 x 的颜色,以后你就是 x 的人了
in[st[tot]]=0;
}while(st[tot--]!=x);//pop until x popped
}
}
Tarjan's BCCs(无向图双连通分量)algorithm
与 SCC 类似,所以符号沿用
注意此时 \(low[x]\) 的定义改变,且图 \(G\) 中不再存在意义上的“横”边、“前”边。
\(low[x]\) 定义为满⾜以下条件之一的节点的最小时间戳:
该点在 \(sbt[x]\) 中。
存在⼀条从该点出发至 \(sbt[x]\) 中任一点的非树边。
剩下的分析和算法就一样了~
桥
桥⼀定是搜索树上的边。
\]
Code by FuZhenTao
const int SIZE=100010;
int head[SIZE],ver[SIZE*2],nxt[SIZE*2];
int dfn[SIZE],low[SIZE],n,m,tot,num;
bool bridge[SIZE*2];
void add(int x,int y){
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void tarjan(int x,int in_edge){
dfn[x]=low[x]=++num;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(!dfn[y]){
tarjan(y,i);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x]) bridge[i]=bridge[i^1]=true;
}
else if(i!=(in_edge^1)){
low[x]=min(low[x],dfn[y]);
}
}
}
int main(){
cin>>n>>m;
tot=1;
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
add(x,y);
add(y,x);
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i,0);
}
for(int i=2;i<tot;i+=2){
if(bridge[i]) cout<<ver[i^1]<<" "<<ver[i]<<endl;
}
}
割点
\]
Code by FuZhenTao
void tarjan(int x){
dfn[x]=low[x]=++num;
int flag=0;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
flag++;
if(x!=root||flag>1) cut[x]=true;
}
}
else low[x]=min(low[x],dfn[y]);
}
}
e-DCC(边双连通分量)
设 \(G\) 是无向连通图。
\]
只需要求出无向图中所有的桥,把桥都删除之后,图会分成若干个连通块,每个连通块就是⼀个"边双连通分量"。
Code by FuZhenTao
int c[SIZE],dcc;
void dfs(int x){
c[x]=dcc;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(c[y]||bridge[i]) continue;
dfs(y);
}
}
for(int i=1;i<=n;i++){
if(!c[i]){
++dcc;
dfs(i);
}
}
v-DCC(点双连通分量)
\]
v-DCC 的求法炒鸡麻烦,鸽了。
由于 Tarjan \(O(n)\) 求 LCA 好像并不是 Tarjan 的算法,而且倍增 \(O(n\log n)\) 好用并好写,所以就不再论述了。
重修 Tarjan的更多相关文章
- HDU4738 tarjan割边|割边、割点模板
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4738 坑点: 处理重边 图可能不连通,要输出0 若求出的结果是0,则要输出1,因为最少要派一个人 #inc ...
- bzoj 1179[Apio2009]Atm (tarjan+spfa)
题目 输入 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一 ...
- tarjan讲解(用codevs1332(tarjan的裸题)讲解)
主要借助这道比较裸的题来讲一下tarjan这种算法 tarjan是一种求解有向图强连通分量的线性时间的算法.(用dfs来实现) 如果两个顶点可以相互通达,则称两个顶点强连通.如果有向图G的每两个顶点都 ...
- NOIP2009最优贸易[spfa变形|tarjan 缩点 DP]
题目描述 C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市.任意两个 城市之间最多只有一条道路直接相连.这 m 条道路中有一部分为单向通行的道路,一部分 为双向通行的道路 ...
- Tarjan
//求强连通分量 void uni(int x,int y){ if (rank[x]<rank[y]){ fa[x]=y; size[y]+=size[x]; }else{ rank[x]+= ...
- 【UOJ#67】新年的毒瘤 Tarjan 割点
#67. 新年的毒瘤 UOJ直接黏贴会炸... 还是戳这里吧: http://uoj.ac/problem/67#tab-statement Solution 看到这题的标签就进来看了一眼. 想 ...
- 【Codefoces487E/UOJ#30】Tourists Tarjan 点双连通分量 + 树链剖分
E. Tourists time limit per test: 2 seconds memory limit per test: 256 megabytes input: standard inpu ...
- 【BZOJ-1123】BLO Tarjan 点双连通分量
1123: [POI2008]BLO Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 970 Solved: 408[Submit][Status][ ...
- 【BZOJ-2730】矿场搭建 Tarjan 双连通分量
2730: [HNOI2012]矿场搭建 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1602 Solved: 751[Submit][Statu ...
随机推荐
- java的stream的使用
过滤 filter: //匹配第一个元素 Optional<Integer> findFirst=list.stream().filter(x->x>6).findFirst( ...
- mysql主要的两个索引Innodb和MyIASM。
参考:https://www.cnblogs.com/xiaohaillong/p/6079551.html 1. a.Innodb引擎,Innodb引擎提供了对数据库ACID事务的支持.并且还提供了 ...
- C++、Java、Python、Linux、Go、前端、算法,慕课资料分享
C++.Java.Python.Linux.Go.前端.算法,慕课资料分享 微信公众号:大道同行JAVA 如有问题或建议,请后台留言,我会尽力解决你的问题. 前言 又见面了.废话不多说,最近多了一些在 ...
- 小程序使用 lodash 的问题
import _ from 'lodash' 报错: vendor.js:11874 Uncaught TypeError: Cannot read property 'prototype' of u ...
- 20210713考试-2021noip14
T1 队长快跑 #include<bits/stdc++.h> using namespace std; const int N=1e6+5,INF=0x7fffffff; int n,a ...
- 自定义-starter
目录 说明 编写启动器 新建项目测试我们自己写的启动器 分析完毕了源码以及自动装配的过程,可以尝试自定义一个启动器来玩玩! 自动装配的过程 SpringBoot-静态资源加载-源码 SpringBoo ...
- <input type="file">如何实现自定义样式
利用样式覆盖来实现效果:先看下原本和改变后的样式 1 <!doctype html> 2 <html> 3 <head> 4 <title>file自定 ...
- 分布式必备理论基础:CAP和BASE
大家好,我是老三,今天是没有刷题的一天,心情愉悦,给大家分享两个简单的知识点:分布式理论中的CAP和BASE. CAP理论 什么是CAP CAP原则又称CAP定理,指的是在一个分布式系统中,Consi ...
- python-引用/模块
导入文件,先从当前目录下找,找不到从环境变量中找 1.导入模块,实质是把制定的py文件执行一遍. 自己写的模块:要导入的文件在当前目录下的:form 文件夹.py文件名 import 函数名 标准模块 ...
- VS Code + WSL 搭建 RaspberryPi Pico 开发环境
前面老周写一堆 .NET 与树莓派相关的水文.其实使用的是.net的 IOT 库,并不只是树莓派,其他运行 Linux 的开发板都适用,只要有 GPIO 就行.老周好像在哪看到过,有 USB 转GPI ...