Tarjan算法之割点

五迷三道 提交于 2019-12-03 05:25:47

定义

(均在无向图中):在一个连通图中,如果有一个顶点,删除这个点以及相关的边之后,连通块的数量增多,我们就称这个顶点为割点.

算法

tarjan。设有点x及其子节点y,如果x不是是该连通图根节点,那么当且仅当存在dfn[x] <= low[y]时,x为割点。如x为根节点,则至少有满足上式的两个y才能保证x为割点。
解释:

  1. x不为根节点,x把在x之前遍历的点和在x后遍历的点隔离,则去掉x会是原图不连通而新增连通块。
  2. x为根节点,存在至少两个y表明走任意一个y都不可能到达其他的y。那x被取走后y之间必然互相独立,增加新连通块。
    注意:由于判定符号是 <=,则不必考虑重边与父节点。

    例题 Luogu-P3388

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
#define lson x<<1
#define rson x<<1|1
#define ll long long
#define rint register int
#define mid ((st[x].l + st[x].r) >> 1)
using namespace std;
template <typename xxx> inline void read(xxx &x) {
    char c = getchar(),f = 1;x = 0;
    for(;c ^ '-' && !isdigit(c);c = getchar());
    if(c == '-') c = getchar(),f = -1;
    for(;isdigit(c);c = getchar()) x = (x<<1) + (x<<3) + (c ^ '0');
    x *= f;
}
template<typename xxx> inline void print(xxx x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
const int maxn = 100010;
const int inf = 0x7fffffff;
const int mod = 1e9 + 7;
struct edge{
    int to,last;
}e[maxn<<1];
int head[maxn],tot;
inline void add(int from,int to) {
    ++tot;
    e[tot].to = to;
    e[tot].last = head[from];
    head[from] = tot;
} 
int n,m;
int dfn[maxn],low[maxn],cnt;
int rt,cut[maxn];
inline void tarjan(int x) {
    low[x] = dfn[x] = ++cnt;
    int ans = 0;
    for(rint i = head[x];i;i = e[i].last) {
        if(!dfn[e[i].to]) {
            tarjan(e[i].to);
            if(low[x] > low[e[i].to]) low[x] = low[e[i].to];
            if(low[e[i].to] >= dfn[x]) {
                ++ans;
                if(x ^ rt || ans > 1) cut[x] = 1;
            }
        } 
        else if(low[x] > dfn[e[i].to]) low[x] = dfn[e[i].to];
    }
}
int main()
{
    read(n);read(m);tot = 1;
    for(rint i = 1;i <= m; ++i) {
        int a,b;
        read(a);read(b);
        if(a == b) continue;
        add(a,b);add(b,a);
    }
    for(rint i = 1;i <= n; ++i) {
        if(!dfn[i]) {
            rt = i;
            tarjan(i);
        }
    }
    int ans = 0;
    for(rint i = 1;i <= n; ++i) if(cut[i]) ++ans;
    print(ans),putchar('\n');
    for(rint i = 1;i <= n; ++i) {
        if(cut[i]) print(i),putchar(' ');
    }
    return 0;
}
/*

*/
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!