tarjan

[Tarjan系列] Tarjan算法与有向图的SCC

£可爱£侵袭症+ 提交于 2019-12-02 23:49:23
前面的文章介绍了如何用Tarjan算法计算无向图中的e-DCC和v-DCC以及如何缩点。 本篇文章资料参考:李煜东《算法竞赛进阶指南》 这一篇我们讲如何用Tarjan算法求有向图的SCC( 强连通分量 )已经如何缩点。 给定一张有向图,若对于图中任意两个节点x和y, 既有x到y的路径,又有y到x的路径,则该有向图是一张“强连通图”。 有向图的极大连通子图被称为“强连通分量”,即SCC。 一个环一定是强连通图。如果既有x到y的路径,又有y到x的路径,那么x和y就一定在一个环中。 这就是Tarjan算法的原理:对于每个点x,找到与它一起能构成环的所有点。 下面介绍有向图中的三种边(x,y): 1. 树枝边:搜索树中x是y的父节点 2. 前向边:搜索树中x是y的祖先节点 3. 后向边:搜索树中y是x的祖先节点 4. 横叉边:除了以上三种情况外的边,满足dfn[y]<dfn[x] 这里只给出简单定义,不再赘述。 我们可以发现,用Tarjan算法求SCC时,后向边(x,y)可以和搜索树上从y到x的路径构成一个环。 除后向边外,通过横叉边也可能找到一条从y出发能回到x的祖先节点的路径。 那么为了找到通过横叉边和后向边构成的环,Tarjan算法在dfs的过程中维护一个栈,当访问到节点x时,栈中需要保存以下两类节点: 1. 搜索树上x的祖先节点,记为集合anc(x)。设y∈anc(x)

[Tarjan系列] Tarjan算法求无向图的桥和割点

删除回忆录丶 提交于 2019-12-02 22:09:20
RobertTarjan真的是一个传说级的大人物。 他发明的LCT,SplayTree这些数据结构真的给我带来了诸多便利,各种动态图论题都可以用LCT解决。 而且,Tarjan并不只发明了LCT,他对计算机科学做出的贡献真的很多。 这一篇我就来以他名字命名的Tarjan算法可以O(n)求出无向图的割点和桥。 进一步可以求出无向图的DCC( 双连通分量 )。不止无向图,Tarjan算法还可以求出有向图的SCC( 强连通分量 )。 Tarjan算法基于dfs,接下来我们引入几个基本概念。 dfn:时间戳 我们对一张图进行深度优先遍历,根据第一次访问到它的时间顺序给它打上一个标记,这个标记就是时间戳。 搜索树: 在一张无向连通图中选定任意一个节点进行深度优先遍历,每个点仅访问一次。所有发生了递归的边会构成一棵树,我们称其为无向连通图的“搜索树”。 追溯值: 除了时间戳,Tarjan算法还引入了另一个概念:“追溯值” low。 我们用subtree(x)表示搜索树中以x为根的子树,low[x]定义为下列节点的时间戳的最小值: 1. subtree(x)中的节点 2. 通过一条不在搜索树上的边,能够到达subtree(x)中的节点 我们来画一个图理解一下:(方便起见,图中的节点编号就是它的时间戳) 图中红色的边就是这张图的搜索树 那么我们容易得出:subtree(2)={4,3}

【图论】tarjan割点:模板题:洛谷3388

点点圈 提交于 2019-12-02 16:15:07
割点 在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多,就称这个点集为割点集合。 割点的求法 由tarjan的算法过程,我们可以得知,若一个点u为割点,则其子孙中必有dfs序比其小的点v,使low[v]<low[u],在去掉这个点u后,必然让强连通分量中的环上一点去掉,则割掉后的子图不能构成强连通分量。 模板题:洛谷3388 求割点的个数和数量 #include<bits/stdc++.h> using namespace std; const int maxn=20010; int low[maxn],dfn[maxn],iscut[maxn]; int n,m,ans; vector<int> g[maxn]; int st[maxn],top; int deep; void tarjan(int u,int fa) { int child=0; int sz=g[u].size(); dfn[u]=low[u]=++deep; for(int i=0;i<sz;i++) { int v=g[u][i]; if(!dfn[v]) { child++; tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>dfn[u]) iscut[u]=1; } else { if(v!

tarjan

痴心易碎 提交于 2019-12-02 12:44:34
用途:缩点+找环 DFN:dfs序编号 low[u]:u点所能到达的,深度最小的点的DFS序编号 DFS序:DFS过程中的顺序,可记录 low【树枝边】:dfs时将要访问的边 (判断标准:该边终点尚未被访问) 【前向边】:终点已被访问过,且在子树中的边 (判断标准:终点已被访问过,且终点dfs序>起点dfs序的边) dfn【后向边】:终点已被访问并在栈中,且不在子树中的边 【横叉边】:终点已被访问并不在栈中,且不在子树中的边 【真正需要判断并用于更新的只有树枝边(用low更新)和后向边(用dfn更新).关于前向边和横叉边……他们死了】 1 #include <iostream> 2 using namespace std; 3 const int maxn=10000; 4 int dfn[maxn];//dfs序 5 int low[maxn];//u点所能到达的,深度最小的点的DFS序编号 6 int dfscnt;//记录dfs序的计时器 7 int s[maxn],p;//栈 8 int scc[maxn];//scc[u]表示强连通分量u的编号 9 int scccnt; //表示强连通分量的数量,用作计数器 10 11 void tarjan(int u){ 12 dfscnt++; 13 dfn[u]=low[u]=dfscnt; 14 s[p++]=u; 15

tarjan求强连通分量入门

醉酒当歌 提交于 2019-12-02 12:14:57
# include <cstring> # include <cstdio> # include <algorithm> # include <cstdlib> # include <stack> using namespace std ; const int maxn = 1e5 + 10 ; struct node { int next , to ; } ; int tot , cnt , deep ; node edge [ maxn << 1 ] ; int dfn [ maxn ] , color [ maxn ] , low [ maxn ] , vis [ maxn ] , num [ maxn ] ; int head [ maxn ] ; stack < int > Q ; void add ( int u , int to ) { edge [ tot ] = ( node ) { head [ u ] , to } ; head [ u ] = tot ++ ; } void tarjan ( int u ) { vis [ u ] = 1 ; dfn [ u ] = ++ deep ; low [ u ] = deep ; Q . push ( u ) ; for ( int i = head [ u ] ; ~ i ; i = edge [ i ] .

UOJ67 新年的毒瘤【Tarjan,割点】

旧时模样 提交于 2019-12-02 12:12:34
Online Judge : #uoj 67 Label :Tarjan,割点,细节 题目描述 辞旧迎新之际,喜羊羊正在打理羊村的绿化带,然后他发现了一棵长着毒瘤的树。这个长着毒瘤的树可以用 \(n\) 个结点 \(m\) 条无向边的无向图表示。这个图中有一些结点被称作是毒瘤结点,即 删掉这个结点 和与之相邻的边之后,这个图会变为一棵树。树也即无简单环的无向连通图。 现在给你这个无向图,喜羊羊请你帮他求出所有毒瘤结点。 输入 第一行两个正整数 \(n,m\) ,表示有 \(n\) 个点 \(m\) 条边。保证 \(n≥2\) 。 接下来 \(m\) 行,每行两个整数 \(v,u\) ,表示 \(v\) 和 \(u\) 之间有一条无向边。 \(1≤v,u≤n\) 。 保证没有重边和自环 。 输出 第一行一个正整数 \(n_s\) ,表示这个图中有 \(n_s\) 个结点是毒瘤。 接下来一行,共 \(ns\) 个整数,每个整数表示一个毒瘤结点的编号。 请按编号从小到大的顺序输出。 数据保证图中 至少存在一个毒瘤结点 。 样例 Input#1 6 6 1 2 1 3 2 4 2 5 4 6 5 6 Output#1 3 4 5 6 Hint 对于40%数据, \(n,m<=1000\) ; 另存在10%数据, \(m=n-1\) ; 另存在20%数据, \(m=n\) ; 对于100

tarjan找有向图强连通分量

寵の児 提交于 2019-12-02 10:49:47
tarjan找强连通分量 有向图强连通分量 在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量。 tarjan找强连通算法 算法思想 首先,一个强连通分量形成一个环,或者是一个单点。 如何知道这个环上的某个点呢?即让这个点在dfs中被遍历到两次即可。 我们知道,图可以用dfs遍历,且dfs采用了“栈”的思想,可以用栈实现对强连通分量上的点的保存。一个点及其子孙中存在极大强连通分量,当且仅当该点是其与子孙各点中dfs序最小的那个,否则,若有一个子孙在它之前被遍历到,则能构成一个更大的强连通分量。 具体实现 变量定义 dfn[N]:某个点的dfs序 low[N]:某个点以及其子孙中dfs序最小的值 color[N]:某个点所属的强连通分量的颜色 st[N]:栈,用于储存可能构成强连通分量的点 vis[N]:某个点是否在栈中 代码 void tarjan(int u) { dfn[u]=low[u]=++deep; vis[u]=1; st[++top]=u; int sz=g[u].size(); for(int i=0;i<sz;i++) { int v=g[u][i

组队传球(点双 边双)

情到浓时终转凉″ 提交于 2019-12-02 10:42:51
问题描述 操场上有n名足球队员,编号1到n。 有的队员是好友关系,队员们只愿意把球传给自己的好友。总共有m对好友关系。 何老板要大家组队玩一种传球游戏,组队的要求是:参加游戏的队员中,任意一对队员间最多能有一次传球,且队员传出去的球没有机会再次传回自己。 何老板向你提出询问:在编号为[L,R]区间中,存在多少个区间[X,Y],有L<=X,Y<=R,并且编号在区间[X,Y]中的队员满足组队的条件。 输入格式 第一行,两个整数n和m 接下来m行,每行两个整数a,b,表示编号为a,b的两个队员是好友 接下来一行,一个整数q,表示何老板共提出了q次询问 接下来q行,每行两个整数L,R,表示一次询问的区间。 输出格式 q行,每行一个整数,表示答案。 解: 恶补了一波tarjan 以前只会用tarjan求强联通分量 这次涨见识了 定义d[i]表示 i 号点最远能到达的点的编号 注意到这是一个单增的函数 容易想到利用二分查找 主要在于求环 ~割点就是将点删掉图不联通 ~桥就是将边删掉图不联通 ~点双联通分量就是将图中存在任意删掉一个点图仍旧是联通的 ~边双联通分量就是将图中存在任意删掉一个边图仍旧是联通的 此题的坑点在于 点可能是割点 处于两个环之中 所以我就会想到利用tarjan 求简单环 也就是满足条件 low[u]<=low[v] v是u的父亲 那么v就是割点 只要依次弹栈 记录最大最小

[usOJ4888]電圧

China☆狼群 提交于 2019-12-02 09:26:16
题目 传送门 题目描述 电路由 N N N 个节点和 M M M 根细长的电阻组成。节点编号为 1 , 2 , 3 , ⋯   , N 1,2,3,\cdots,N 1 , 2 , 3 , ⋯ , N 。 每个节点可设定为两种电平之一:高电平或者低电平。每个电阻连接两个节点,只有一端是高电平,另一端是低电平的电阻才会有电流流过。两端都是高电平或者低电平的电阻不会有电流流过。 试求:有多少个电阻,可以通过调节各节点的电压,使得「没有电流流经该电阻,且其他 M − 1 M-1 M − 1 根电阻中都有电流流过」。 输出输出格式 (就是你想的那样) 数据范围与约定 对于所有测试数据, 2 ≤ N ≤ 1 0 5 , 1 ≤ M ≤ 2 × 1 0 5 2\le N\le 10^5,1\le M\le 2\times 10^5 2 ≤ N ≤ 1 0 5 , 1 ≤ M ≤ 2 × 1 0 5 ,不保证图是连通的,不保证没有重边。 思路 本质是类似 二分图 的东西。说白了就是找到 所有奇环的公共边 。 但是与完全删掉这条边不同。这条边 不能在偶环上 。否则就会……(试一试就知道了) 然后要找到一个环。不用 tarjan \text{tarjan} tarjan 等神奇算法,只需要建一个 dfs \text{dfs} dfs 树,然后考虑 一条 非树边与树边构成的环。 为什么不用考虑两条边的

tarjan求LCA学习笔记

允我心安 提交于 2019-12-02 07:53:59
tarjan求LCA学习笔记 其实,实质就是离线处理询问, 我们可以将所有询问看成类似于这样的样子, (图之后再补上) 每次提高LCA,当两点恰好连通时,两点之间最高的点就为LCA。 具体实施: 每次dfs搜索所有子树,搜索完后将子树内所有点的父亲标为祖辈中连通的最高点,搜到询问点时,若另一点已经搜过,LCA就为它祖辈中连通的最高点。 (图之后再补上) void tarjan(int x,int fa){ int v; for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa) tarjan(e[i].to,x),f[e[i].to]=x; for(int i=head2[x];i;i=q[i].nxt){ v=q[i].to; if(book[v]) lca[q[i].w]=getf(v); } book[x]=1; } for(int i=1;i<=m;++i){ t1=read(),t2=read(),que[i].x=t1,que[i].y=t2; if(t1==t2){lca[i]=t1; continue;} add(t1,t2,i,q,head2),add(t2,t1,i,q,head2); } tarjan(1,0); 来源: https://www.cnblogs.com/ljk123-de-bo-ke/p/11736090