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 ...
随机推荐
- 欢迎收看 Flink Forward Asia 2021 峰会直播
在线收看地址:https://developer.aliyun.com/special/ffa2021/live#?utm_content=g_1000316459 Flink 是由 Apache 软 ...
- react中使用styled-component
styled-component的使用地址(https://www.cnblogs.com/aichenxy/p/8672752.html)
- vue3.0+vite+ts项目搭建-postcss-pxtorem 实现移动自适应(五)
这里不考虑大屏,所以不做amfe-flexible的配置 首先是安装依赖 yarn add postcss-loader postcss-pxtorem -D yarn add autoprefixe ...
- python多环境管理一(venv与virtualenv)
一.背景 我们经常会遇见这样的场景: 1.各个项目使用的python版本不相同 由于Python的解释器版本众多,各版本之间差异非常大.特别是python2和python3,互不兼容. 有些项目可能用 ...
- 《剑指offer》面试题21. 调整数组顺序使奇数位于偶数前面
问题描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分. 示例: 输入:nums = [1,2,3,4] 输出:[1,3,2,4] ...
- 使用 fail2ban 保护 frp 服务
背景 我们一般会使用 fail2ban 来保护暴露到公网的提供密码登录的 ssh 连接等. 但使用 frp 穿透后所有的从外网访问都会变成 127.0.0.1 进入的,原本能用 fail2ban 保护 ...
- WebGPU相关的资源学习和社区
我在网上搜寻了很多关于WebGPU相关的资料: #我觉得首先在B站上看到的徐博士免费教程非常好,讲解详细,并且评论回复比较快,都会有回应,徐博士B站网址:https://space.bilibili. ...
- Servlet-通过继承HttpServlet类实现Servlet程序
通过继承HttpServlet类实现Servlet程序(开发一般用) 一般在实际项目开发中,都是使用继承 HttpServlet类的方式实现Servlet程序 1,编写一个类去继承 HttpServl ...
- 集合框架-HashSet存储自定义对象
1 package cn.itcast.p4.hashset.test; 2 3 import java.util.HashSet; 4 import java.util.Iterator; 5 6 ...
- 集合框架-工具类-Collections-排序
1 package cn.itcast.p2.toolclass.collections.demo; 2 3 import java.util.ArrayList; 4 import java.uti ...