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

Tarjan求点双连通分量

时间:2024-10-25 20:44:13浏览次数:5  
标签:nxt Tarjan 连通 int 割点 bcnt low 求点

更新日志

思路

首先,点双连通分量就是删去任意一点后仍连通的分量。

如何求呢?看着定义,答案就已经在里面了——求出割点即可。

边双不同的是,点双中是包括割点的。

究其原因,删去割点之后,原图会被分成多个连通块,但事实上,割点加入其中任意一个,仍然是双连通的。证明如下:

  • 若连通块到割点有多条边,毫无疑问。
  • 若割点是独立点,那么它单独为一个点双。
  • 若连通块到割点只有一边——额外,设这个连通块在割点下面,也就是说,它属于割点的某一棵子树——那么,这个子结点也是一个割点,这两个割点可以构成一个双连通分量。

所以,一个割点可以同时属于所有被它割的双连通分量——包括它的父节点所在的连通块。

我们可以考虑在找割点的时候,实时进行判断,假如找到了一个子树被当前节点割,那么就把它们加到一个双连通分量中。

不会找割点点这里

细节

由于我们要找分量,毫无疑问,就需要借助栈,来获取它的子树。

需要注意的是,由于当前节点同时属于多个连通分量,所以在弹出的时候不应该把它自己也弹出,只需要把被它割走的子树全部弹出。别忘了最后额外把当前节点也加入那个连通块之中。

与找割点不同的是,我们不需要额外地判断根节点是否是一个割点,因为即使根节点只有一个子树,它同样满足上述性质,并且需要被加入连通块中。

换种说法,就算根节点只有一棵子树,我们也需要把它加入它子树的连通块中。

额外地,我们需要特殊判定独立点,因为它没有子树,无法在算法中自动加入连通块。

任何对找连通块原理不理解的都可以看求强连通分量来借鉴思路。不过那是有向图,所以要判断是不是 \(双向奔赴\) ,不要和点双边双搞混了。

使用栈储存,记得在一轮Tarjan结束后额外弹出根节点——你应该没忘这句话吧?

需要注意的是,由于当前节点同时属于多个连通分量,所以在弹出的时候不应该把它自己也弹出,只需要把被它割走的子树全部弹出。别忘了最后额外把当前节点也加入那个连通块之中。

模板

int dcnt;
int dfn[N],low[N];
bool flag[N];
stack<int> stk;
int bcnt;
vector<int> bcc[N];
void tarjan(int x,int fa){
    dfn[x]=low[x]=++dcnt;
    stk.push(x);
    int ccnt=0;
    for(int e=hd[x];e;e=ne[e]){
        int nxt=to[e];
        if(!dfn[nxt]){
            ++ccnt;
            tarjan(nxt,x);
            low[x]=min(low[x],low[nxt]);
            if(low[nxt]>=dfn[x]){
                ++bcnt;
                while(1){
                    int tp=stk.top();stk.pop();
                    bcc[bcnt].push_back(tp);
                    if(tp==nxt)break;
                }
                bcc[bcnt].push_back(x);
            }
        }else low[x]=min(low[x],dfn[nxt]);
    }
    if(x==fa&&ccnt==0){
        ++bcnt;
        bcc[bcnt].push_back(x);
    }
}

例题

LG8435

代码

前注:非题解,不做详细讲解

#include<bits/stdc++.h>
using namespace std;

typedef vector<int> veci;

const int N=5e5+5,M=2e6+5;

int n,m;

int cnt;
int hd[N],ne[M*2],to[M*2];
void adde(int a,int b){
    to[++cnt]=b;
    ne[cnt]=hd[a];
    hd[a]=cnt;
}

int dcnt;
int dfn[N],low[N];
bool flag[N];
stack<int> stk;
int bcnt;
vector<int> bcc[N];
void tarjan(int x,int fa){
    dfn[x]=low[x]=++dcnt;
    stk.push(x);
    int ccnt=0;
    for(int e=hd[x];e;e=ne[e]){
        int nxt=to[e];
        if(!dfn[nxt]){
            ++ccnt;
            tarjan(nxt,x);
            low[x]=min(low[x],low[nxt]);
            if(low[nxt]>=dfn[x]){
                ++bcnt;
                while(1){
                    int tp=stk.top();stk.pop();
                    bcc[bcnt].push_back(tp);
                    if(tp==nxt)break;
                }
                bcc[bcnt].push_back(x);
            }
        }else low[x]=min(low[x],dfn[nxt]);
    }
    if(x==fa&&ccnt==0){
        ++bcnt;
        bcc[bcnt].push_back(x);
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>m;
    int a,b;
    for(int i=1;i<=m;i++){
        cin>>a>>b;
        adde(a,b);
        adde(b,a);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]){
            tarjan(i,i);
            stk.pop();
        }
    }
    cout<<bcnt<<"\n";
    for(int i=1;i<=bcnt;i++){
        cout<<bcc[i].size()<<" ";
        for(auto j:bcc[i])cout<<j<<" ";
        cout<<"\n";
    }
    return 0;
}

标签:nxt,Tarjan,连通,int,割点,bcnt,low,求点
From: https://www.cnblogs.com/HarlemBlog/p/18503249

相关文章

  • Tarjan求边双联通分量
    更新日志思路首先,我们求出原图中所有的桥,然后跑DFS给原图分区。求桥具体过程:Tarjan求桥更具体的,我们遍历每一个点,假如这个点没有被分区,那么就从这个点开始深搜。每一次深搜,都走不是桥的边,那么走到的就都属于一个边双。(很容易证明)这样,我们把每一次深搜走到的所有点分成一......
  • Tarjan求割边(桥)
    更新日志思路割边定义与割点相似,不过是把点换成了边,所以思想和割点差不多。Tarjan割点我们只需要在Tarjan过程中判断某一颗子树的low是否严格大于当前节点的dfn。值得注意,这里子树的low不应该由到它的原边回溯到它的父节点得到!究其原因,其实就是如果子树是一个强连通分量,那......
  • 连通性相关
    强连通强连通:有向图\(G\)中每个点中可互相到达。强连通分量:极大的强连通。(最大可能的)求强连通分量先跑出图的DFS搜索树(黑边)。一个结论:一个强连通分量一定在该强连通分量中的第一个被访问的点的子树内。设根为\(u\),考虑若存在一个点\(v\)不在\(u\)子树中则一定存......
  • 插头 dp / 轮廓线 dp / 连通性 dp 做题笔记
    牢游看见我正在做插头dp,于是给我了一个Claris的连通性dp的pdf。好了,现在又有可以软性颓废的事可干了。好多题目在其他平台都找不到了,这时候我们becoder的优越性就体现出来了!(这就是到处搬题的好处)所以大部分题目链接都会放becoder的链接。什么?你不知道becoder或者没......
  • 「图::连通」详解并查集并实现对应的功能 / 手撕数据结构(C++)
    目录概述成员变量创建销毁根节点访问路径压缩启发式合并复杂度Code概述并查集,故名思议,能合并、能查询的集合,在图的连通性问题和许多算法优化上着广泛的使用。这是一个什么数据结构呢?一般来讲,并查集是由一系列集合组成的集合群。其中,每个集合都有一个根节点,它的......
  • Leetcode 1135. 最低成本连通所有城市
    1.题目基本信息1.1.题目描述想象一下你是个城市基建规划者,地图上有n座城市,它们按以1到n的次序编号。给你整数n和一个数组conections,其中connections[i]=[x_i,y_i,cost_i]表示将城市x_i和城市y_i连接所要的cost_i(连接是双向的)。返回连接所有城市的最低成本,......
  • 浅谈 tarjan
    就是记录两个数组:dfn[]和low[]其中dfn[]表示访问的顺序,low[u]用来存储\(u\)不经过其父亲能到达的最小时间戳。。。搬一下wiki的图。。。我们发现\(low[v]\gedfn[u]\)可以表示不能回到祖先,则\(u\)点位割点。。。直接上代码P3388------>#include<bits/stdc++.h>usi......
  • Tarjan求强连通分量
    思路首先建成DFS树,对于每个节点,储存两个信息:dfn与low。每当遍历到一个节点,就将其压入栈中,作用后面会说。dfn储存一个节点的dfs序,low储存一个节点的子树能够到达的在栈中的最小dfs序。首先有一个性质,每个节点子树中所有节点的dfn必然大于当前节点的。那么,假如一个节点的low小......
  • 【题解】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:......