tarjan

tarjan强连通图分量

丶灬走出姿态 提交于 2019-12-19 13:05:09
[有向图强连通分量]   有向图强连通分量的Tarjan算法 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。   下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。    [Tarjan算法]   Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。   定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。   当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。 [演示]   从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时,DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连通分量。   返回节点5,发现DFN[5]=LOW[5],退栈后{5}为一个强连通分量。   返回节点3,继续搜索到节点4,把4加入堆栈

2015长春网络赛总结

a 夏天 提交于 2019-12-19 03:29:47
  早上七点多就(冻)醒来了,训练了一个暑假,acm生涯的第一场网络赛,很激动。   九点开打,我拔不出网线,用的机房电脑,装的cb有问题,不能编译,只好用dev。男神电脑插上网线没有网,习惯了linux可能不习惯吧。这提醒我们以后一定要早点去把环境调好。   第三分钟,G题有人A了。我跟560开始看题,男神还在弄电脑。题意是给你n个数(n<1000),然后q(q<1000)次询问,要求你输出[l,r]区间的最大值。数据很小,我说暴力,然后560说线段树,然后模板13分钟1Y。然后560开始搞J,一个貌似是一个组合数学。好像是模板题。然后我开始看B。一开始我想枚举每一个点,然后模拟删边。然后脑子不知道为啥突然走岔了,突然觉得直接tarjan模板就好。啪啪啪敲完,过了样例,交上去wa了。后来560提醒了一下,发现假设是两个三角形并且有一条边相连,tarjan算法会把它当做两个强连通分量。想了一会,于是重写了bfs删边,然后再加一个tarjan算法,不判父节点。然后就可以处理了。应该有更好的方法,可能是我傻逼。然后11点15A了这个题。这个时候560一直在调J,他说kuangbin的模板好像有点问题,然后一直在网上找模板(事实证明是自己瓜了)。然后男神写A题的模拟一直超时,于是抽空看了一下E,给我讲了一下E题的题意。看数据,感觉是并查集,因为之前一场训练赛打了ZOJ3659

bzoj2730矿场搭建(Tarjan割点)

五迷三道 提交于 2019-12-16 14:45:15
2730: [HNOI2012]矿场搭建 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 1771 Solved: 835 [ Submit ][ Status ][ Discuss ] Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。 Input 输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。 Output 输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写, Case 与 i 之间有空格, i 与 :之间无空格, : 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i

tarjan有向图的强连通分量

旧城冷巷雨未停 提交于 2019-12-06 08:35:40
  有向图强 连通分量 :在 有向图 G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点 强连通 (strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个 强连通图 。有向图的极大强连通子图,称为强连通分量(strongly connected components)。   通过对强连通分量的缩点,可以将任意一个有向图变成一个有向无环图(DAG)。 我们将边分为四类:1.树枝边(x是y的父亲结点)2.前向边(x是y的祖先结点)3.后向边(x是y的子孙结点)4.横叉边(连向其他分支的并且已经搜过的边) 可以看出,树枝边是前向边的特殊情况。 如何判断x所在的位置在哪个强连通分量中? 情况1: 存在一条边后向边,指向祖宗结点。 情况2: 先由该点通过横叉边走到另一个分支,再由分支走到该点的某个祖宗结点上。 这里用tarjan算法求强连通分量。我们引入一个时间戳的概念,如上图,用dfs对其进行编号。 对每个点定义两个时间戳dfn[u]和low[u]表示从u开始走,所能遍历到的最小时间戳是什么。如果u是其所在的强联通分量重的最高点,等价于 dfn[u] == low[u] 。 可以证明,通过dfs搜图,能得到该图拓扑图的逆序,tarjan就是按照dfs的顺序搜索,所以

最近公共祖先

帅比萌擦擦* 提交于 2019-12-06 03:21:56
裸的求公共祖先的题目。 思路:用被增法求最近公共祖先,用fa[i][j]表示从i开始,向上走2j 步能走到的所有节点,其中1 <= j <= logn(下取整) 例如上图,f[6][0] = 4, f[6][1] = 2, f[6][2] = -1表示不存在。 做法:1.首先我们需要预处理一个fa数组,采用递推的方式。fa[i][j]表示从i开始向上走2 j 步,那么我们可以拆成两部分,先走2 j -1 步再走2 j - 1 步,也就是fa[fa[i][j - 1][j - 1]。于是我们便得到递推公式 fa[i][i] = fa[fa[i][j - 1]][j - 1] 。只需将j从小到大枚举一遍就可以了。 ps:本质是用二进制拼凑路径长度,和多重背包的二进制优化思想大致。 2.同时我们还需要预处理一个depth数组,来表示当前点的深度,例如depth[1] = 1.depth[6] = 4。就是到根节点的距离+1。同时我们再设置两个“哨兵“,如果从i跳过了根节点,那么 fa[i][j] = 0, depth[0] = 0 。 3.预处理完两个数组后,进行操作,先将两个点跳到 同一层 ,我们就统一将a看做较低的节点。b看做较高的节点。 4.两个节点所在层数相同后,如果还不是公共祖先,就一起往上跳到最近公共祖先的 下一层 。(比较好判断,因为当f[a][k] = f[b][k]时

Tarjan

拥有回忆 提交于 2019-12-05 15:11:29
有向图的强连通分量:两个点如果能够相互到达,那么称他们相互强连通。若一个有向图的所有点对都是相互强连通的,那么称之为强连通图。一个有向图的极大强连通子图称为该图的强连通分量。 无向图的割点/边:去掉该点/边之后无向图的连通性发生改变的点/边称为割点/边。 无向图的点/边双连通分量:若一个无向图不存在割点/边,则称作点/边双连通图。一个无向图的极大点/边双连通子图称为该图的点/双连通分量。 强连通分量记为scc,点双连通分量记为v-dcc,边双连通分量记为e-dcc,双连通分量统一记为dcc。 缩点 遍历到时把点加入栈。 然后如果回溯到某个点时 \(low_u=dfn_u\) ,那么我们栈上面的一部分(弹完 \(u\) 为止)就是一个scc。 注意Tarjan求出来的scc编号恰好是拓扑序反序。 我们可以把每个scc缩成一个点,那么我们可以得到一个DAG。 然后我们就可以在这个DAG上面根据拓扑序DP了。 割点 如果一个点 \(u\) 存在一个出点 \(v\) ,满足 \(low_v\ge dfn_u\) ,那么 \(u\) 就是该图的一个割点。 如果是dfs的根节点,那么需要存在两个触点满足上述条件才是一个割点。 而两个v-dcc之间由割点连接,而且有且仅有一个割点。 所以一个割点可能属于多个v-dcc。 v-dcc的维护需要圆方树,这里就不做研究了。 割边(桥) 如果一条边 \(

Tarjan-割点

余生长醉 提交于 2019-12-05 09:29:12
割点——tarjan 1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int MAXN = 20001; 5 const int MAXM = 100001; 6 int n, m; 7 int ans;//个数 8 9 10 int head[MAXN], cnt, v[2 * MAXM], nxt[2 * MAXM]; 11 void add(int x, int y) { 12 nxt[++cnt] = head[x]; 13 head[x] = cnt; 14 v[cnt] = y; 15 } 16 17 18 int dfn[MAXN], low[MAXN], ind; 19 int cut[MAXN]; 20 21 22 void tarjan(int now, int fa) { 23 dfn[now] = low[now] = ++ind; 24 int child = 0; 25 for (int i = head[now]; i ; i = nxt[i]) { 26 if(!dfn[v[i]]) { 27 tarjan(v[i], now); 28 low[now] = min(low[now], low[v[i]]); 29 30 if(low[v[i]] >= dfn[now] &&

LuoguP4306连通数 Tarjan

只谈情不闲聊 提交于 2019-12-04 04:11:00
https://www.luogu.org/problem/P4306 https://www.lydsy.com/JudgeOnline/problem.php?id=2208 这道题里面还有缩点+拓扑排序 话不多说直接上代码,代码里有详细解释,看不懂的先去看看搜索树方面的知识,lyd书上有 #include <bits/stdc++.h> using namespace std; int n, ans, cnt, sum, tot;//tot记录图中有几个强连通分量 bool v[2021];//判断节点是否被访问过 int dfn[2021];//节点i搜索的次序编号(时间戳)每个点第一次被访问的时间顺序 int low[2021];//表示u或u的子树能够追溯到的最早的栈中节点的次序号,时间戳的最小值 int scc[2021];//表示 x 所在的强连通分量的编号 int deg[2021];//储存强连通分量的入度 int head[2021];//一开始的图 int head2[2021];//保存缩点后新的有向无环图 int stk[2021], top;//数组模拟栈,top记录栈中元素个数 int sze[2021];//记录每个强连通分量中元素个数 bitset<150021> f[100021]; queue<int> q; struct edge{ int

强连通分量Tarjan算法模板

别说谁变了你拦得住时间么 提交于 2019-12-04 04:07:05
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<string> #include<vector> #include<cstring> #include<iomanip> #include<sstream> #include<iostream> #include<algorithm> #define INF 0x3f3f3f3f #define ls l,m,rt<<1 #define rs m+1,r,rt<<1|1 //#define ll __int64 //#define int ll //typedef long long ll; typedef unsigned long long ull; const int MAXN=5e5+10; const int MOD=1e9+7; const double eps=1e-6; using namespace std; //-------------------------------------------// vector<int> g[MAXN]; stack<int> s; int dfn[MAXN],low[MAXN],instack[MAXN],cnt,k;

P5025 [SNOI2017]炸弹 题解

≯℡__Kan透↙ 提交于 2019-12-04 03:38:05
蒟蒻的第一篇黑题题解(学了这么长时间了才第一道也是没谁了。) 题目链接 ; Solution : 朴素:  根据题目描述,我们可以处理每一个x节点左右爆炸半径范围内的点,然后模拟一次爆炸 (for),遍历每一个点。每当我们遍历到一个点,我们就对这个点在进行一次处理半径+dfs直到没有能遍历的了,直接统计遍历的点数然后输出。 时间复杂度:O(n^2)级别。 于是乎:    是不是可以暴力踩标算了呢? 显然完全接受不了。 因为这个算法要考虑到连边操作。对于每一个点,我们要将它自己连向能够遍历到的边,那么意味着,一个边要被连很多次。 就如下图: 其中编号为1的节点能炸到1 2 3 4 5,2能炸到1 2 3,3能炸到1 2 3 4 5,其中2 3 4挺惨的。 于是有了这么一个线段树建图的思路: 考虑线段树的性质,上层节点可以代表下层节点的一段区间,那么我们是不是也可以建这类的上层边,表示通过这条边能遍历到它对应的下层边的所有点 。 (就是这句精华) 这样我们先把所有点进行从1到n的编号,然后跑线段树的build建边,然后依次对每一个点进行从底层(只能这么理解)到上层节点的连边表示能遍历到上层节点能遍历到的节点。 这样的话,会形成很多环。在对每一个点都进行范围连边以后,对于一个环(或者强连通分量)内的点,自然能互相遍历到,为了方便统计