1. 程式人生 > >P3388 【模板】割點(割頂)

P3388 【模板】割點(割頂)

  • 對於非根節點,判斷是不是割點就有些麻煩了。我們維護兩個陣列dfn[]和low[],dfn[u]表示頂點u第幾個被(首次)訪問,low[u]表示頂點u及其子樹中的點,通過非父子邊(回邊),能夠回溯到的最早的點(dfn最小)的dfn值(但不能通過連線u與其父節點的邊)。對於邊(u, v),如果low[v]>=dfn[u],此時u就是割點。
  • 但這裡也出現一個問題:怎麼計算low[u]。
  • 假設當前頂點為u,則預設low[u]=dfn[u],即最早只能回溯到自身。
  • 有一條邊(u, v),如果v未訪問過,繼續DFS,DFS完之後,low[u]=min(low[u], low[v]);
  • 如果v訪問過(且u不是v的父親),就不需要繼續DFS了,一定有dfn[v]<dfn[u],low[u]=min(low[u], dfn[v])。
  • #include<bits/stdc++.h>
    using namespace std;
    #define maxn 200010
    struct node
    {
        int v,to;
    } edge[maxn];
    int n,m,ind,cnt,tot;
    int head[maxn],dfn[maxn];
    int low[maxn],a,b;
    bool cut[maxn];
    void add(int x,int y)
    {
        edge[++cnt].v=y;
        edge[cnt].to=head[x];
        head[x]=cnt;
    }
    void tarjan(int u,int root)
    {
        dfn[u]=low[u]=++ind;
        int children=0;
        for(int i=head[u]; i!=-1; i=edge[i].to)
        {
            int v=edge[i].v;
            if(!dfn[v])
            {
                tarjan(v,root);
                low[u]=min(low[u],low[v]);
                if(low[v]>=dfn[u]&&u!=root)
                    cut[u]=1;
                if(u==root)
                    children++;
            }
            low[u]=min(low[u],dfn[v]);
        }
        if(children>=2&&u==root)
            cut[u]=1;
    }
    int main()
    {
        memset(dfn,0,sizeof(dfn));
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&a,&b);
            add(a,b);
            add(b,a);
        }
        for(int i=1; i<=n; i++)
            if(dfn[i]==0)
                tarjan(i,i);
        for(int i=1; i<=n; i++)
            if(cut[i])
                tot++;
        printf("%d\n",tot);
        for(int i=1; i<=n; i++)
            if(cut[i])
                printf("%d ",i);
        return 0;
    }