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

luogu題解 P3388 【【模板】割點(割頂)】

題解 搜索樹 cpp 沒有 head 判斷 algo 以及 clu

外加定義:在一個無向圖中,如果刪掉點 x 後圖的連通塊數量增加,則稱點 x 為圖的割點。

外加圖示

技術分享圖片

開始思路為割橋上的點為割點,後來證明的確正確。
不過可惜的是他的逆定理錯了(gg了),不過數據很弱以至於得了90分。

如圖所示

技術分享圖片

圖中無割橋,但點3卻是割點,貌似無法解決。

(順及客串my blog
,以及圖論的必要工具

回歸正題,另一種思路誕生了:
如果u點的子節點為v,v點他能返回的最老祖先比u點年輕或一樣(即dfn[u]值<=low[v]),那麽如果刪去u點,那麽v以下的點就會與v以上的點失去聯系,就會產生新的連通塊(實質是在我的原來思路上多了一個判斷

也就是說如果在我們的搜索樹上有一個點只有樹邊與祖先相連,而沒有反向邊連回祖先節點的話,那麽它就是割點。就是沒有這樣的邊

技術分享圖片

至於實現方法貌似蒟蒻所知只有Tarjan。

這道題是模板題,大家還是不要抄代碼為好。(事關今後的Tarjan生涯)

代碼

#include<cstdio>
#include<algorithm>
#include<stack>
#include<cstring>
#define Max 1000000+199
using namespace std;
int n,m,dfn[Max]={0},low[Max],cast[Max],ins[Max],inx=0,head[Max],v[Max]={0},cnt=0,gs=0,cd[Max]={0};
stack<int> s;
struct edge
{
    int c,to,next;
}e[Max];
void adde(int a,int b)
{
    cnt++;
    e[cnt].to=b;
    e[cnt].c=a;
    e[cnt].next=head[a];
    head[a]=cnt;
    cd[a]++;
}
int ans=0,gd[Max]={0};
void tarjan(int x,int fa)
{
    int u,sk=0;
    inx++;
    dfn[x]=low[x]=inx;
    s.push(x);
    ins[x]=1;
    for(int i=head[x];~i;i=e[i].next)
    {   
        u=e[i].to;
        if(dfn[u]==0)
        {
            tarjan(u,fa);
            if(low[u]>=dfn[x]&&x!=fa)gd[x]=1;
            v[i]=v[i%2==0?i-1:i+1]=1;
            low[x]=min(low[x],low[u]);
            if(x==fa)sk++;
        }
        else if(ins[u]==1&&v[i]==0)v[i]=v[i%2==0?i-1:i+1]=1,low[x]=min(low[x],dfn[u]);
    }
    if(dfn[x]==low[x])
    {
        gs++;
        u=Max;
        while(u!=x)
        {
            u=s.top();
            s.pop();
            ins[u]=0;
            cast[u]=gs;
            //printf("%d %d\n",u,gs);
        }
        
    }
    if(x==fa&&sk>=2)gd[x]=1;
}
int main()
{
    memset(cd,0,sizeof(cd));
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        adde(a,b);
        adde(b,a);
        //printf("%d",v[i]);
    }
    for(int i=1;i<=n;i++)
    if(dfn[i]==0)tarjan(i,i);
    for(int i=1;i<=n;i++)
    {
        if(gd[i]==1)ans++;
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;i++)
    {
        if(gd[i]==1)printf("%d ",i);
    }
    return 0;
}

luogu題解 P3388 【【模板】割點(割頂)】