tarjan2
反过来调过去,我还是感觉没学明白缩点
- 讲一个有向图中的所有强连通分量缩成一个点后,构成的新图是一个DAG。
- 一个点所在的强连通分量一定被该点所在DFS搜索树所包含
- 树上的边大致分为:树枝边,前向边(从上往下指),后向边(从下往上指),横叉变。其中前向边肉眼可见地没什么卵用
接下来开始算法流程。
- tarjan的精髓如上次所说,在于DFS搜索树,在DFS搜索树中强连通分量以怎样形式存在是关键问题。对于x,存在祖宗y,从x出发可经过横叉边,返祖边,后向边到达y,则x,y属于同一强连通分量。操作中记录最小 y 为:low(x)=dfn(y)。(其中单点也算强连通块)如果有一个dfn_x=low_x ,那么就是说 x 在一个新的强连通块里,同理,low_x的初始也就是dfn_x。
- 我们用一个栈来维护 已经被遍历过的、还未确定隶属哪个强连通分量的 点,在该栈中越靠栈顶DFS序越靠后(是栈底元素的后代)。
- 关于low_x的求法、更新。考虑如何求low_x:low_x 可能被更新,当且仅当x连出了一条树枝边,横叉边或后向边。设该边连向点 v
1. 树枝边: low_x= min(low_x,low_v) v 到达的点x一定可以到达,且v与x有祖宗关系
2. 后向边: low_x= min(low_x,low_v) v 的祖先一定是 x 的祖先
3. 横叉边:此时分两种情况考虑的
当 v 点已经退栈时,那么点v可到达的DFS序最小的祖先不是x的祖先,对 low_x 没有贡献; 当点v还在栈中时,v 点可到达的DFS序最小的祖先是x的祖先,有 low_x=min(low_x,low_v) (点v可到达的DFS序最小的祖先一定是x的,v 点能到达的点,x一定能到达) 特别地,由于前向边的更新对于求强连分量没有帮(更新是重复的),所以我们也可以有 low_x=min(low_x,low_v)
那么我们只需判断点 x 连出的边是哪一条就可以转移了。显然,当 dfn_v=0 时(此时v未被访问过),这是一条树枝边。我们再维护一个 col 数组, col_i 表示点 i 所在的强连通分量,在点 i 退栈时,我们对col进行赋值,那么当 dfn_v≠0&&col_v=0 时,点v一定在栈中(后向边指向的点一定在栈中,横叉边指向的点满足此条件时在栈中,而前向边是否存在与答案无关),此时用 low_x=min(low_x,low_v) 转移即可,否则无需转移。该算法时间复杂度为(n+m),因为深度优先遍历每个点只会经过一次,每条边也只会访问一次,而每个点都只会进/出栈一次,所以总时间复杂度为(n+m)
//把一个点当成根提溜出来,抖搂抖搂成一棵树
void dfs(int u)
{
//记录dfs序
//可通过任意多dfs边与最多一条非树返祖边到达的、本强连通分量内最小点
dfn[u]=low[u]=++dfs_clock;
s.push(u);
for(int v:g[u])
{
if(!dfn[v])//树边
{
dfs(v);
low[u]=min(low[u],low[v]);
}
else if(!sccnum[v])//返祖
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
scccnt++;//强连通块+1
while(1)
{
int x=s.top();
s.pop();
sccnum[x]=scccnt;
sccsz[scccnt]++;
if(x==u) break;
}
}
}
tarjan2的更多相关文章
- 校际联合Contest
每次开一个坑都像是重新被碾压的预感 最近的新闻,以前很喜欢乔任梁的<复活>...然后他就死了...感觉我再多愁善感一点的话...就要悲伤逆流成河了吧... Contest 09/24(乐滋 ...
- Tarjan系列算法总结(hdu 1827,4612,4587,4005)
tarjan一直是我看了头大的问题,省选之前还是得好好系统的学习一下.我按照不同的算法在hdu上选题练习了一下,至少还是有了初步的认识.tarjan嘛,就是维护一个dfsnum[]和一个low[],在 ...
- tarjan解决路径询问问题
好久没更新了,就更一篇普及组内容好了. 首先我们考虑如何用tarjan离线求出lca,伪代码大致如下: def tarjan(x): 将x标记为已访问 for c in x的孩子: tarjan(c) ...
- bzoj 2730: [HNOI2012]矿场搭建【tarjan】
先tarjan找割点和点双连通分量,然后对一个点双,如果没有割点,那么需要建立两个出口(割掉一个另一个备用):如果只有一个割点,出口可以设立在任意一个非割点的地方:如果有两个及以上个割点,就不用建出口 ...
- JZOJ 5246. 【NOIP2017模拟8.8A组】Trip(trip)
5246. [NOIP2017模拟8.8A组]Trip(trip) (File IO): input:trip.in output:trip.out Time Limits: 1500 ms Memo ...
- 各色Tarjan集合
#include<bits/stdc++.h> using namespace std; const int N=100000,M=200000; //所有Tarjan都要: // dfn ...
随机推荐
- MySQL主从复制作用和配置
一.复制概述 Mysql内建的复制功能是构建大型,高性能应用程序的基础.将Mysql的数据分布到多个系统上去,这种分布的机制,是通过将Mysql的某一台主机的数据复制到其它主机(slaves)上,并重 ...
- [转]Vue之引用第三方JS插件
1.绝对路径引入,全局使用. 在index.html文件中使用script标签引入插件. 该种方式就是上面演示ckplayer插件使用的方式. 备注: 这种方式的引用,会在开启ESLint时,报错,可 ...
- vant引入及配置
1. vant 官网 https://youzan.github.io/vant/#/zh-CN/quickstart 2. 通 npm 安装 npm i vant -S 3.安装 babel-plu ...
- Go 分布式令牌桶限流 + 兜底策略
上篇文章提到固定时间窗口限流无法处理突然请求洪峰情况,本文讲述的令牌桶线路算法则可以比较好的处理此场景. 工作原理 单位时间按照一定速率匀速的生产 token 放入桶内,直到达到桶容量上限. 处理请求 ...
- 【解决了一个小问题】go.mod文件中引用另一个库,总会自动拉取新版本
我的项目依赖某个旧的公共库: require ( git.xxx.com/myprj/mylib v0.0.43 ) 可以编译的时候,系统总会自动加上这样的路径: require ( git.xxx. ...
- 【代码分享】用redis+lua实现多个集合取交集并过滤,类似于: select key from set2 where key in (select key from set1) and value>=xxx
redis中的zset结构可以看成一个个包含数值的集合,或者认为是一个关系数据库中用列存储方式存储的一列. 需求 假设我有这样一个数据筛选需求,用SQL表示为: select key from set ...
- Python36 使用Redis 构建分布式爬虫(未完)
很长时间未更新了,人懒了. 最近有不少的东西,慢慢写吧,最近尝试了一下python 使用Redis 来构建分布式爬虫: 单体爬虫有很多缺点,但是在学习过程中能够学习爬虫的基本理念与运行模式,在后期构建 ...
- Python 安装MySQL 错误处理
正常情况下如果使用python 连接数据库需要安装 python-MySQL 类库 #pip install python-MySQL 等待安装完成即可 使用时 import MySQLdb ==== ...
- 3D建模服务提供更高效、专业的能力,“筑”力开发者
3D建模服务(3D Modeling Kit)是HMS Core在图形图像领域又一技术开放.3D建模产品的定位就是要做快速.简洁.低成本的3D制作能力,并陆续开放给有3D模型.动画游戏制作等能力诉求的 ...
- linux中rpm安装
目录 一:linux中rpm安装 1.rpm简介 2.区别 3.RPM命令五种基本模式 二:RPM安装全面解析 1,下载软件包 2, 安装软件包 3, 尝试卸载 4, 更新(升级) 5,软件包名称: ...