洛谷【p3388】割點(tarjan)

題目背景

割點

題目描述

給出一個n個點,m條邊的無向圖,求圖的割點。

輸入輸出格式

輸入格式:
第一行輸入n,m

下面m行每行輸入x,y表示x到y有一條邊

輸出格式:
第一行輸出割點個數

第二行按照節點編號從小到大輸出節點,用空格隔開

輸入輸出樣例

輸入樣例#1:
6 7
1 2
1 3
1 4
2 5
3 5
4 5
5 6
輸出樣例#1:
1
5
說明

n,m均爲100000

tarjan 圖不一定聯通!!!

思路:模板題

割點定義:在一個無向連通圖中刪去這個點,圖會變得不連通,則該點爲割點。

題解:

#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;
const int maxn=200000+10;
struct cc{
    int from,to;
}es[maxn];
int first[maxn],nxt[maxn];
int tot=0;
void build(int ff,int tt)
{
    es[++tot]=(cc){ff,tt};
    nxt[tot]=first[ff];
    first[ff]=tot;
}
int dfn[maxn],low[maxn];
int cnt=0,scc_cnt=0;
int pd[maxn];
bool vis[maxn];
int dfs(int u,int fa)
{
    low[u]=dfn[u]=++cnt;
    int cd=0;//統計兒子個數 
    for(int i=first[u];i;i=nxt[i])
    {
        int v=es[i].to;
        if(!dfn[v])
        {
            cd++; 
            low[v]=dfs(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            //如果u的子樹中最早能訪問到的結點都比他後訪問,則u可能是割點 
            {
                if(!vis[u])
                {   
                    pd[u]=1;
                    vis[u]=1;
                }
            }
        }
        else if(v!=fa)
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(fa==-1&&cd==1)//如果是該點是根節點且只連向一個點,則不是割點 
    { 
        pd[u]=0;
    }
    return low[u];
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        build(u,v);
        build(v,u);
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
        {
            dfs(i,-1);
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(pd[i])
        {
            ans++;
        }
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;i++)
    {
        if(pd[i])
        {
            printf("%d ",i);
        }
    }
    return 0;
}
發佈了131 篇原創文章 · 獲贊 254 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章