[模板] tarjan/联通分量/dfs树
边的分类
有向图
有向图边分为四类: 树边, 前向边, 返祖边(后向边), 横叉边.
上图:
判定
对图进行dfs, 不考虑已经遍历过的点, 得到dfs序 \(dfn_i\).
在dfs过程中, 记录当前dfs栈. 对于边\((u,v)\),
- 树边: \(vis_v=0\);
- 前向边: \(vis_v=1\) 且 \(dfn_v > dfn_u\);
- 返祖边: \(vis_v=1\) 且 \(dfn_v < dfn_u\), 且 \(v\) 在当前栈内;
- 横叉边: \(vis_v=1\) 且 \(dfn_v < dfn_u\), 且 \(v\) 不在当前栈内.
无向图
如上图, 边仅分为两种:
- 树边: \(vis_v=0\) ;
- 前向边/返祖边, 这两个其实是同一条边. 可以通过 dfs 序区分.
有向图的强连通分量 && 缩点 (Tarjan)
Tarjan算法寻找有向图的强连通分量 – Miskcoo's Space
简介
Tarjan 强连通分量算法可以找出图的所有强连通分量, 并为每个点标记所在的强连通分量.
定义:
- \(dfn_i\) : \(i\) 节点的dfs序;
- \(low_i\) : \(i\) 节点通过最多一条返祖边能到达的最小dfs序.
- 栈, 维护:
- 当前节点到根的链;
- 在这条链中节点所在的强连通分量中, 且之前遍历过的点. (是在这个节点的子树中的一些节点)
- \(vi_i\): 表示节点是否在栈内.
应用
缩点.
tarjan求出的强连通分量标号为逆拓扑序.
代码
int dfn[nsz],low[nsz],pd=0,scc[nsz],ps=0;
int stk[nsz],top=0,vi[nsz];
// get scc
void tar(int p){
dfn[p]=low[p]=++pd;
stk[++top]=p,vi[p]=1;
for(int i=g1.hd[p],v;i;i=g1.edge[i].pr){
v=g1.edge[i].t;
if(dfn[v]==0){
tar(v);
low[p]=min(low[p],low[v]);
}
else if(vi[v])low[p]=min(low[p],dfn[v]); // 返祖边; 横叉边不更新
}
if(low[p]==dfn[p]){
++ps;
do{
vi[stk[top]]=0;
scc[stk[top]]=ps;
}while(stk[top--]!=p);
}
}
//缩点
void sol(){
rep(i,1,n)if(scc[i]==0)tar(i);
rep(i,1,n){
for(int j=g1.hd[i],v;j;j=g1.edge[j].pr){
v=g1.edge[j].t;
if(scc[i]!=scc[v])g2.adde(scc[i],scc[v]);
}
}
}
其他
事实上, 将 \(low_i\) 定义为 "\(i\) 节点通过任意条返祖边能到达的最小dfs序" 也是合法的, 即将
else if(vi[v])low[p]=min(low[p],dfn[v]);
改为
else if(vi[v])low[p]=min(low[p],low[v]);
不难发现这两个做法是等价的; 但在求双连通分量时这么写是错误的.
无向图的边双连通分量,割边,缩点
点双连通分量为对点的划分.
求边双/缩点代码和强连通分量几乎相同.
栈中维护的是:
- 当前点到根的路径
- 当前点所在点双中的点
//由于可能有重边, 搜索时传递的应为e(fa,p)这条边, 而非父亲这个点
int dfn[nsz],low[nsz],pd=0,ebcc[nsz],pb=0;
int stk[nsz],top=0,vi[nsz];
void tar(int p,int efa){
dfn[p]=low[p]=++pd;
stk[++top]=p,vi[p]=1;
forg(p,i,v){
if(i==(efa^1))continue;
if(dfn[v]==0){
tar(v,i); //warning
low[p]=min(low[p],low[v]);
}
else low[p]=min(low[p],dfn[v]);
}
if(dfn[p]==low[p]){ //efa is cut edge
++pb;
do{
ebcc[stk[top]]=pb;
vi[stk[top]]=0;
}while(stk[top--]!=p);
}
}
int in[nsz];
int sol(){
rep(i,1,n)if(dfn[i]==0)tar(i,0);
rep(i,1,n){
forg(i,j,v){
if(ebcc[v]!=ebcc[i])++in[ebcc[v]]; //a cut edge
}
}
}
无向图的点双连通分量,割点,缩点
点双连通分量为对边的划分, 因此栈中存边, 而非点.
割点 \(\iff\) 属于多个点双的点.
所有点双和割点形成一棵树结构.
缩点: 在新图中对每个割点和点双各建一个点, 将每个割点连向所在的点双.
代码较长, 但比较容易理解.
//cf962f
//无自环
int dfn[nsz],pd=0,low[nsz],iscut[nsz],curbcc[nsz],pbcc=0;
int bccsz[nsz];
set<int> inbcc[nsz];
pair<int,int> stk[msz];
int top=0;
void color(int p,int pbcc){
if(curbcc[p]!=pbcc){
inbcc[p].insert(pbcc);
curbcc[p]=pbcc;
++bccsz[pbcc];
}
}
//could not find
void tarj(int p,int fe){
dfn[p]=low[p]=++pd;
for(int i=hd[p],v;i;i=edge[i].pr){
if(i==fe)continue;
v=edge[i].t;
if(dfn[v]==0){
stk[++top]=make_pair(p,v);
tarj(v,i^1);
low[p]=min(low[p],low[v]);
if(low[v]>=dfn[p]){//点双
iscut[p]=1,++pbcc;
int x,y;
do{
x=stk[top].first,y=stk[top].second,--top;
color(y,pbcc);
}while(!(x==p&&y==v));
color(p,pbcc); //一条边属于一个点双
}
}
else if(dfn[v]<dfn[p]){//返祖边
low[p]=min(low[p],dfn[v]);
stk[++top]=make_pair(p,v);
}
}
if(fe==0){
if(hd[p]==0)color(p,++pbcc); //只有一个点的图
if(edge[hd[p]].pr==0)iscut[p]=0;//度数<=1的根不是割点
}
}
//debug
rep(i,1,n){
printf("p=%d iscut=%d\n",i,iscut[i]);
for(int v:inbcc[i])printf("%d ",v);
printf("\n");
}
//3 2 1 2 2 3
//p=1 iscut=0
//2
//p=2 iscut=1
//1 2
//p=3 iscut=0
//1
//6 5
//1 6 6 2 6 3 2 4 2 5
//p=1 iscut=0
//5
//p=2 iscut=1
//2 3 4
//p=3 iscut=0
//1
//p=4 iscut=0
//3
//p=5 iscut=0
//2
//p=6 iscut=1
//1 4 5
//5 6
//1 2 1 3 3 4 5 3 5 1 3 5
//p=1 iscut=1
//2 3
//p=2 iscut=0
//3
//p=3 iscut=1
//1 2
//p=4 iscut=0
//1
//p=5 iscut=0
//2
[模板] tarjan/联通分量/dfs树的更多相关文章
- tarjan模板 强联通分量+割点+割边
// https://www.cnblogs.com/stxy-ferryman/p/7779347.html ; struct EDGE { int to, nt; }e[N*N]; int hea ...
- 算法模板——Tarjan强连通分量
功能:输入一个N个点,M条单向边的有向图,求出此图全部的强连通分量 原理:tarjan算法(百度百科传送门),大致思想是时间戳与最近可追溯点 这个玩意不仅仅是求强连通分量那么简单,而且对于一个有环的有 ...
- [J]computer network tarjan边双联通分量+树的直径
https://odzkskevi.qnssl.com/b660f16d70db1969261cd8b11235ec99?v=1537580031 [2012-2013 ACM Central Reg ...
- BZOJ 压力 tarjan 点双联通分量+树上差分+圆方树
题意 如今,路由器和交换机构建起了互联网的骨架.处在互联网的骨干位置的核心路由器典型的要处理100Gbit/s的网络流量. 他们每天都生活在巨大的压力之下.小强建立了一个模型.这世界上有N个网络设备, ...
- 无向图边双联通分量 tarjan 模板
#include <bits/stdc++.h> using namespace std; const int MAXN = 100005; const int MAXM = 500005 ...
- 『Tarjan算法 无向图的双联通分量』
无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...
- Tarjan 强连通分量 及 双联通分量(求割点,割边)
Tarjan 强连通分量 及 双联通分量(求割点,割边) 众所周知,Tarjan的三大算法分别为 (1) 有向图的强联通分量 (2) 无向图的双联通分量(求割点,桥) ...
- 图连通性【tarjan点双连通分量、边双联通分量】【无向图】
根据 李煜东大牛:图连通性若干拓展问题探讨 ppt学习. 有割点不一定有割边,有割边不一定有割点. 理解low[u]的定义很重要. 1.无向图求割点.点双联通分量: 如果对一条边(x,y),如果low ...
- Tarjan的强联通分量
求强联通分量有很多种. <C++信息学奥赛一本通> 中讲过一个dfs求强联通分量的算法Kosdaraju,为了骗字数我就待会简单的说说.然而我们这篇文章的主体是Tarjan,所以我肯定说 ...
随机推荐
- &,^,|,的简化计算与理解
(全部和2进制有关 , 凡是2的次方数都是独立数列,都要先分解再计算的,该计算方式仅供手工计算理解,电脑会自动进行换算的) (第二个等号后面为2进制的结果,不够位在前面补0,1为真,0为假) A^ ...
- 80后程序员降薪6K,预感中年危机来袭,准备跳槽却碰壁
一提及程序员,很多人想到的都是“工资高”“技术好”诸如此类的,可见程序员是个非常赚钱的职业,所以每年都会有很多毕业生来选择这个行业. 但是社会是公平的,不要只看程序员表面上的光鲜亮丽,其背后也有很多的 ...
- 从零讲解搭建一个NIO消息服务端
本文首发于本博客,如需转载,请申明出处. 假设 假设你已经了解并实现过了一些OIO消息服务端,并对异步消息服务端更有兴趣,那么本文或许能带你更好的入门,并了解JDK部分源码的关系流程,正如题目所说,笔 ...
- 在 DotNetCore 3.0 程序中使用通用协议方式启动文件关联应用
问题描述 在传统的基于 .NET Framework 的 WPF 程序中,我们可以使用如下代码段启动相关的默认应用: # 启动默认文本编辑器打开 helloworld.txt Process.Star ...
- springMVC实现增删改查
首先需要准备好一张数据库表我这里用emp这张表:具体代码: /* SQLyog 企业版 - MySQL GUI v8.14 MySQL - 5.1.73-community ************* ...
- 自己实现的typeOf函数1
自己实现的typeOf函数:返回传入参数的类型 主要用于解决,js自带的typeof返回结果不精确:Ext JS中typeOf对字符串对象.元素节点.文本节点.空白文本节点判断并不准确的问题 js代码 ...
- Odoo 开源微信小程序商城模块
详见:http://oejia.net/blog/2018/09/13/oejia_weshop_about.html oejia_weshop Odoo 微信小程序商城模块 oejia_weshop ...
- mpvue学习笔记-之微信小程序数据请求封装
简介 美团出品的mpvue已经开源出来很久了,一直说要进行一次实践,这不最近一次个人小程序开发就用上了它. 看了微信官方的数据请求模块--request,对比了下get和post请求的代码,发现如果在 ...
- SQLServer修改登陆账户信息
修改登陆账户信息注意事项 如果 CHECK_POLICY设置为ON,则无法使用 HASHED参数. 如果 CHECK_POLICY更改为ON,则将出现以下行为: 用当前的密码哈希值初始化密码历史记录. ...
- RabbitMQ消息模型概览(简明教程)
小菜最近用到RabbitMQ,由于之前了解过其他消息中间件,算是有些基础,所以随手从网上搜了几篇文章,准备大概了解下RabbitMQ的消息模型,没想到网上文章千篇一律,写一大堆内容,就是说不明白到底怎 ...