首页 > 其他分享 >Tarjan求强连通分量

Tarjan求强连通分量

时间:2024-10-15 11:32:50浏览次数:1  
标签:rt Tarjan 连通 dfn low 当前 节点 求强

思路

首先建成DFS树,对于每个节点,储存两个信息:dfn与low。

每当遍历到一个节点,就将其压入栈中,作用后面会说。

dfn储存一个节点的dfs序,low储存一个节点的子树能够到达的在栈中的最小dfs序

首先有一个性质,每个节点子树中所有节点的dfn必然大于当前节点的。

那么,假如一个节点的low小于当前节点dfn,就说明它的子树中存在边通向当前节点的上级,当前分量就不是极大强连通分量,因为它可以与它的上级节点构成一个环,那么当前子树中的强连通分量就可以与上级节点合并。

同理,若一个节点的low等于当前节点dfn,那么就说明当前节点的子树中最长存在一个通向当前节点的环,就是一个极大强连通分量,不可能有其它节点加入了。

会不会存在一个节点的子树中存在有一块节点是单独的极大强连通分量呢?存在的,所以我们需要在dfs的同时进行操作,这样,当遍历到一个极大强连通分量子树时,就先把它标记为强连通分量,并且它的dfn就是它本身,绝对大于其父节点,不会对其父节点产生任何影响。

所以,在找到一段强连通分量,就要把它删除,防止加入其父节点的强连通分量之中。

感性想象一下,当前节点的子树中,不与当前节点连通的节点全都删除了,剩下的节点,都可以通过通向当前节点的返祖边回到当前节点,就产生了一个个环,就是一个极大强连通分量了。

在栈中,当前节点的子树全都压在当前节点上面,直接一个个弹出并标记就完事了。

细节

当前节点通向的节点有三种情况:

  1. 没有被遍历过。那么就把这个节点作为自己的子节点,dfs后用其low更新当前节点的low。因为当前节点存在一条通向这个节点的路径,所以这个节点能通向的节点当前节点也可以前往。\(寇可往我亦可往\)
  2. 被遍历过,并且还在栈中。那么,还是有两种情况:
  • 返祖边,是当前节点的祖先,用它的dfn来更新当前节点的low。更具体的,这就产生了一个环,包括了从当前点到祖先点的整条路径,都属于同一个强连通分量。
  • 横叉边,因为它还在栈中,就说明这个节点必然存在在一个环中,有路径通向两颗子树的公共祖先节点,那么也就形成了一个环。按理来说,我们应该用它的low来更新这个节点的low,但因为通向的节点被先遍历过,通向的节点dfn必然大于这个节点所在子树的所有节点dfn,因此直接用其dfn来更新当前节点dfn也是一样的效果,都比当前子树所有节点dfn小。
    综上,就用其dfn来更新当前节点dfn即可。
  1. 被遍历过,并且没在栈中。那么,那个节点就已经完成了。既然当前节点并没有成为它的子节点,就说明那个点没有通向这个点的边。\(单向奔赴是没有结果的\),所以不用处理,二者必然不属于同一个强连通分量,因为无法从对方通向这里。

模板

int scnt;//强连通编号
int scc[N],siz[N];//储存一个节点属于的强连通分量、强连通分量的大小
int dcnt;//dfn编号
int dfn[N],low[N];//见思路
bool ins[N];//是否在栈中
stack<int> stk;
void tarjan(int rt){
    low[rt]=dfn[rt]=++dcnt;
    stk.push(rt);ins[rt]=true;
    for(int i=hd[rt];i;i=ne[i]){
        if(!dfn[to[i]]){
            tarjan(to[i]);
            low[rt]=min(low[rt],low[to[i]]);
        }
        else if(ins[to[i]])low[rt]=min(low[rt],dfn[to[i]]);
    }
    if(dfn[rt]==low[rt]){
        scnt++;
        while(stk.top()!=rt){
            int tp=stk.top();stk.pop();
            ins[tp]=false;
            scc[tp]=scnt;
            siz[scnt]++;
        }
        stk.pop();
        ins[rt]=false;
        scc[rt]=scnt;
        siz[scnt]++;
    }
}

标签:rt,Tarjan,连通,dfn,low,当前,节点,求强
From: https://www.cnblogs.com/HarlemBlog/p/18467103

相关文章

  • 【题解】Solution Set - NOIP2024集训Day50 图的连通性相关
    【题解】SolutionSet-NOIP2024集训Day50图的连通性相关https://www.becoder.com.cn/contest/5618「JSOI2012」越狱老虎桥简述题意:题目大意:给定一张图,A先添加\(1\)条边,B再删去一条边使得图不连通,A要最大化删除边的权值,B要最小化删除边的权值,问最终的权值是多少。......
  • Tarjan缩点题单 刷题题解
    Tarjan缩点可以将一个图的每个强连通分量缩成一个点,然后构建新图,该图就会变成一个有向无环图。变成有向无环图之后就能结合最短路,拓扑......解决相应题目洛谷题单分享:https://www.luogu.com.cn/training/526565前几道是绿题,没什么好写的,大致过一下1.强连通分量题目链接:https:......
  • 连通性问题大杂烩
    前言连通性问题确实时一大比较难啃得蛋糕,每次都要先学习一遍,还不如一次学到通无向图的连通性问题求割点连通图:连通图内的所有点都可以互相到达割点:将割点删掉后整张图不连通定理1:一个点s是割点,当且仅当s作为该连通图的根时,会把连通图分为不相连的几部分定理2:一个非根节点......
  • $Tarjan$强连通分量
    有向图缩点非常板,先缩点再拓扑。其实\(Tarjan\)强连通分量缩点往往与拓扑排序求最长路(或其他)密切相关。有向图缩点问有向图上哪个点,其它点都能走到它题面,先缩点,看缩完后有哪些点出度为\(0\),若有多个,则无解,否则即为那一个。最大半联通子图题面先缩点,可以发现缩点后最大半联通......
  • Tarjan
    强连通分量前置知识强连通:一张有向图的节点两两互相可达。连通分量:若\(H\)是\(G\)的一个连通子图,且不存在\(F\)使得\(H\subsetneqF\subseteqG\),则\(H\)为\(G\)的一个连通分量(也叫连通块)Tarjan求强连通分量DFS生成树(用的OI-wiki的图)树边:图中的黑边,每次搜索找到一......
  • 连通性相关
    一些概念连通:无向图中的任意两点都可以互相到达。强连通:有向图中的任意两点都可以互相到达。连通分量:无向图的极大连通子图。强连通分量:有向图的极大强连通子图。DFS生成树:对一张图进行深度优先遍历得到的生成树。树边:在DFS生成树上的边。前向边:由子树的根连向子树内的......
  • tarjan
    强连通分量SSC(缩点)有向图缩点(把一个强连通分量看成一个点),用于优化。树枝边:DFS时经过的边,即DFS搜索树上的边反祖边:也叫回边或后向边,与DFS方向相反,从某个结点指向其某个祖先的边横叉边:从某个结点指向搜索树中另一子树中的某结点的边,它主要是在搜索的时候遇到了......
  • 强联通分量——Tarjan算法
    Tarjan算法详解参考文章:强连通分量Tarjan算法是一种用于寻找有向图中强联通分量(StronglyConnectedComponents,SCCs)的算法。它是由RobertTarjan在1972年提出的,基于深度优先搜索(DFS)和栈的数据结构。基本概念强联通分量:在一个有向图中,如果一组节点中任意两个节点都可以互相......
  • Tarjan算法缩点
    Tarjan算法缩点一.Tarjan算法缩点详解在图论中,缩点是指将有向图中的强联通分量(SCCs)缩成单个节点,从而得到一个更简单的图结构,称为缩点图或SCC图。Tarjan算法不仅可以用来寻找强联通分量,还可以用来进行缩点操作。基本概念强联通分量:在一个有向图中,如果一组节点中任意两个节点都......
  • Tarjan再学习
    蓝书的那一套理论比较生硬,且不是很深刻,故重学Tarjan。AlexWei《图论Ⅰ》相关定义割点:在无向图中,删去使得连通分量数增加的点被称为割点。割边:在无向图中,删去使得连通分量数增加的边被称为割边。点双连通图:不存在割点的无向图。边双连通图:不存在割边的无向图。点双连通分量:一......