tarjan

tarjan算法

旧街凉风 提交于 2020-10-28 08:10:57
简介 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。 直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为O(N^2+M)。更好的方法是Kosaraju算法或Tarjan算法,两者的时间复杂度都是O(N+M)。本文介绍的是Tarjan算法。 (注:双向遍历方法可以参考算法导论图论中深度优先遍历部分,主要是利用了拓扑排序和转置图) 算法伪代码 Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。 定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。由定义可以得出 Low(u)=Min { DFN(u), Low(v),(u,v)为树枝边,u为v的父节点 DFN(v),(u,v)为指向栈中节点的后向边(非横叉边) } 当DFN(u)=Low(u)时

Tarjan算法求强连通分量

落花浮王杯 提交于 2020-04-07 07:25:54
一、操作过程:tarjan算法的基础是DFS。我们准备两个数组Low和Dfn。Low数组是一个标记数组,记录该点所在的强连通子图所在搜索子树的根节点的 Dfn值(很绕嘴,往下看你就会明白),Dfn数组记录搜索到该点的时间,也就是第几个搜索这个点的。根据以下几条规则,经过搜索遍历该图(无需回溯)和 对栈的操作,我们就可以得到该有向图的强连通分量。 1.数组的初始化:当首次搜索到点p时,Dfn与Low数组的值都为到该点的时间。 2.堆栈:每搜索到一个点,将它压入栈顶。 3.当点p有与点p’相连时,如果此时(时间为dfn[p]时)p’不在栈中,p的low值为两点的low值中较小的一个。 4.每当搜索到一个点经过以上操作后(也就是子树已经全部遍历)的low值等于dfn值,则将它以及在它之上的元素弹出栈。这些出栈的元素组成一个强连通分量。 5.继续搜索(或许会更换搜索的起点,因为整个有向图可能分为两个不连通的部分),直到所有点被遍历。 二、操作原理:由于每个顶点只访问过一次,每条边也只访问过一次,我们就可以在O(n+m)的时间内求出有向图的强连通分量。但是,这么做的原因是什么呢? 1.Tarjan算法基于定理:在任何深度优先搜索中,同一强连通分量内的所有顶点均在同一棵深度优先搜索树中。也就是说,强连通分量一定是有向图的某个深搜树子树。 2.可以证明,当一个点既是强连通子图Ⅰ中的点

专题训练之强连通分量

北城余情 提交于 2020-04-04 04:23:18
tarjan模板 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=20010; 6 const int maxm=50010; 7 struct edge{ 8 int to,nxt; 9 }edge[maxm]; 10 int head[maxn],tot; 11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn]; 12 int index,top; 13 int scc; 14 bool vis[maxn]; 15 int num[maxn]; 16 17 void addedge(int u,int v) 18 { 19 edge[tot].to=v; 20 edge[tot].nxt=head[u]; 21 head[u]=tot++; 22 } 23 24 void tarjan(int u) 25 { 26 int v; 27 low[u]=dfn[u]=++index; 28 stack[top++]=u; 29 vis[u]=true; 30 for ( int i=head[u];i!=-1;i=edge[i].nxt ) { 31 v=edge[i].to; 32

tarjan算法求最近公共祖先

陌路散爱 提交于 2020-04-04 04:20:32
tarjian算法 LCA: LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点。也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们就是要求找到公共的节点中,深度尽量深的点。还可以表示成另一种说法,就是如果把树看成是一个图,这找到这两个点中的最短距离。 LCA算法有在线算法也有离线算法,所谓的在线算法就是实时性的,而离线算法则是要求一次性读入所有的请求,然后在统一得处理。而在处理的过程中不一定是按照请求的输入顺序来处理的。说不定后输入的请求在算法的执行过程中是被先处理的。 tarjan算法。这个算法是基于并查集和DFS的。离线算法。 现在我们来观察正在处理与x结点关联的询问时并查集的情况。由于一个结点处理完毕后,它就被归到其父结点所在的集合,所以在已经处理过的结点中(包括 x本身),x结点本身构成了与x的LCA是x的集合,x结点的父结点及以x的所有已处理的兄弟结点为根的子树构成了与x的LCA是father[x]的集合,x结点的父结点的父结点及以x的父结点的所有已处理的兄弟结点为根的子树构成了与x的LCA是father[father[x]]的集合……(上面这几句话如果看着别扭,就分析一下句子成分,也可参照右面的图)假设有一个询问(x,y)(y是已处理的结点),在并查集中查到y所属集合的根是z,那么z

最近公共祖先LCA Tarjan算法

冷暖自知 提交于 2020-04-04 04:18:28
LCA算法: LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点。也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们就是要求找到公共的节点中,深度尽量深的点。还可以表示成另一种说法,就是如果把树看成是一个图,这找到这两个点中的最短距离。 LCA算法有在线算法也有离线算法,所谓的在线算法就是实时性的,比方说,给你一个输入,算法就给出一个输出,就像是http请求,请求网页一样。给一个 实时的请求,就返回给你一个请求的网页。而离线算法则是要求一次性读入所有的请求,然后在统一得处理。而在处理的过程中不一定是按照请求的输入顺序来处理 的。说不定后输入的请求在算法的执行过程中是被先处理的。 本文先介绍一个离线的算法,就做tarjan算法。这个算法是基于并查集和DFS的。Dfs的作用呢,就是递归,一次对树中的每一个节点进行处理。而并查集的作用就是当dfs每访问完(注意,这里是访问完)到一个点的时候,就通过并查集将这个点,和它的子节点链接在一起构成一个集合,也就是将并查集中的pnt值都指向当前节点。这样就把树中的节点分成了若干个的集合,然后就是根据这些集合的情况来对输入数据来进行处理。 比方说当前访问到的节点是u,等u处理完之后呢,ancestor[u]就构成了u的集合中的点与u点的LCA,而ancestor[fa[u]]就构成

专题训练之双连通

蹲街弑〆低调 提交于 2020-03-29 07:45:11
桥和割点例题+讲解:hihocoder1183 http://hihocoder.com/problemset/problem/1183 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<set> 6 using namespace std; 7 const int maxn=1005; 8 const int maxm=200010; 9 struct edge{ 10 int to,nxt; 11 bool cut; 12 }edge[maxm*2]; 13 int head[maxn],tot; 14 int low[maxn],dfn[maxn]; 15 int index,n,bridge; 16 set<int>st; 17 bool cut[maxn]; 18 19 void addedge(int u,int v) 20 { 21 edge[tot].to=v; 22 edge[tot].nxt=head[u]; 23 edge[tot].cut=false; 24 head[u]=tot++; 25 } 26 27 void tarjan(int u,int pre) 28 { 29 low[u]=dfn[u]=++index; 30

【模板】tarjan

不羁的心 提交于 2020-03-24 07:16:35
int dfn[maxn]; //第i个点被dfs到次序 int low[maxn]; //二叉搜索树上i所在子数上仍在栈中的最小dfn,low[i]相等的点在一个强连通分量中 bool vis_tarjan[maxn]; stack<int>s_tarjan; int tot_tarjan; int cnt_tarjan; int res_tarjan[maxn]; //储存强连通分量,res[i]表示点i属于第res[i]个强连通分量 int num_tarjan[maxn]; //第i个强连通分量的点数 void tarjan(int x) { dfn[x] = low[x] = ++cnt_tarjan; s_tarjan.push(x); vis_tarjan[x] = true; for (int i = head[x]; ~i; i = e[i].nxt) { int v = e[i].v; if (!dfn[v]) { tarjan(v); low[x] = min(low[x], low[v]); } else if (vis_tarjan[v]) { low[x] = min(low[x], dfn[v]); } } if (low[x] == dfn[x]) { tot_tarjan++; while (!s_tarjan.empty() && s

tarjan算法求LCA

这一生的挚爱 提交于 2020-03-15 06:46:07
tarjan算法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先。 这里我们使用tarjan算法离线算法解决这个问题。 离线算法,是指首先读入所有的询问(求一次LCA叫做一次询问),然后重新组织查询处理顺序以便得到更高效的处理方法。Tarjan算法是一个常见的用于解决LCA问题的离线算法,它结合了深度优先遍历和并查集,整个算法为线性处理时间。 总思路就是每进入一个节点u的深搜,就把整个树的一部分看作以节点u为根节点的小树,再搜索其他的节点。每搜索完一个点后,如果该点和另一个已搜索完点为需要查询LCA的点,则这两点的LCA为另一个点的现在的祖先。 1.先建立两个链表,一个为树的各条边,另一个是需要查询最近公共祖先的两节点。 2.建好后,从根节点开始进行一遍深搜。 3.先把该节点u的father设为他自己(也就是只看大树的一部分,把那一部分看作是一棵树),搜索与此节点相连的所有点v,如果点v没被搜索过,则进入点v的深搜,深搜完后把点v的father设为点u。 4.深搜完一点u后,开始判断节点u与另一节点v是否满足求LCA的条件,满足则将结果存入数组中。 5.搜索完所有点,自动退出初始的第一个深搜,输出结果。 如上图,根据实现算法可以看出,只有当某一棵子树全部遍历处理完成后,才将该子树的根节点标记为有颜色

P5008 [yLOI2018] 锦鲤抄(Tarjan+贪心)

时光总嘲笑我的痴心妄想 提交于 2020-03-10 08:20:55
洛谷 题意: 给出一个有向图,每次可以删除存在入度的点及其出边,每次删除一个点可以获得其权值。 问最终能够获得的最大权值为多少。 思路: 考虑DAG:我们直接倒着拓扑序来选,即可将所有入度不为 \(0\) 的点选完。 若不为DAG,考虑 \(tarjan\) 求出强连通分量,分析可以发现:对于一个单独的强连通分量,假设其点数为 \(n\) ,那么可以选择 \(n-1\) 个点;若其入度不为 \(0\) ,那么强连通分量中所有点都可以选择。 然后直接这样来搞就行。 证明...我也不会,在纸上画画就行了。 #include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() #define INF 0x3f3f3f3f // #define Local #ifdef Local #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0) void err() { std::cout << '\n'; } template<typename T, typename...Args

[POI2005]SKA-Piggy Banks tarjan 水题

眉间皱痕 提交于 2020-03-07 13:32:37
Description Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个存钱罐的钥匙放到了某些存钱罐里. Byteazar 现在想买一台汽车于是要把所有的钱都取出来. 他想尽量少的打破存钱罐取出所有的钱,问最少要打破多少个存钱罐. Input 第一行一个整数 N (1 <= N <= 1.000.000) – 表示存钱罐的总数. 接下来每行一个整数,第 i+1行的整数代表第i个存钱罐的钥匙放置的存钱罐编号. Output 一个整数表示最少打破多少个存钱罐. 正解是并查集,专门卡 $tarjan$ 的空间 #include<bits/stdc++.h> #define maxn 1000002 using namespace std; void setIO(string s) { string in=s+".in"; freopen(in.c_str(),"r",stdin); } stack<int>S; int scc,cnt,edges; int low[maxn],pre[maxn],vis[maxn],bin[maxn],du[maxn]; vector<int>G[maxn]; void tarjan(int u) { S.push(u); low[u]=pre[u]=++scc; vis[u]=1; for