tarjan求割点

#include<bits/stdc++.h>
using namespace std;
const int N=2e4+7,M=1e5+7;
int n,m,cnt=0,ind=0;
int dfn[N],low[N],fa[N];
int head[N],to[M*2],nxt[M*2];
bool vis[N];
vector<int> a;
inline void add(int x,int y) {
    nxt[++cnt]=head[x];
    head[x]=cnt; to[cnt]=y;
}
void tarjan(int u) {
    vis[u]=true;
    dfn[u]=low[u]=++ind;
    int ch=0;
    for (int i=head[u];i!=-1;i=nxt[i]) {
        int v=to[i];
        if (!vis[v]) {
            ch++; fa[v]=u;
            tarjan(v);
            low[u]=min(low[u],low[v]);
            if (fa[u]==-1 && ch>=2 || fa[u]!=-1 && low[v]>=dfn[u]) {
                a.push_back(u);
            }
//if放循环里,只要有一个儿子满足条件即为割点
//对于根节点,计算其子树数量,如果有2棵即以上的子树,就是割点。
//对于非根节点,dfn[]表示访问序数,low[]表示u和u子树中能回溯到的最早的点,如果low[u]>=dfn[u],则u为割点
        }
        else low[u]=min(low[u],dfn[v]); //访问过的点应比较dfn[]的值
    }
}
int main() {
    scanf("%d%d",&n,&m);
    memset(head,-1,sizeof(head));
    for (int i=1;i<=m;i++) {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    memset(vis,false,sizeof(vis));
    for (int i=1;i<=n;i++) {
        if (!vis[i]) {
            fa[i]=-1; tarjan(i);
        }
    }
    sort(a.begin(),a.end());
    a.erase(unique(a.begin(),a.end()),a.end());
    printf("%d\n",a.size());
    for (int i=0;i<a.size();i++) printf("%d ",a[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42149421/article/details/86651852
今日推荐