1. 程式人生 > >【洛谷P3388】【模板】割點

【洛谷P3388】【模板】割點

都快忘了割點怎麼搞了

對所有點分兩類

1.根節點 2.非根節點

顯然根節點是很好做的 只需要數一下有沒有兩個子樹以上

對於非根節點 利用tarjan演算法

回憶到dfn的定義:時間戳,即在dfs中第幾個被訪問到 low:經過最多一條後向邊/棧中橫叉邊能到達的最小的節點時間戳

對於當前節點now來說,把整個圖分成了兩個子樹。假如low[vis]>=dfn[now],(注意不要把時間戳和編號序搞混) 也就是說vis節點最多隻能追溯到now這棵子樹以內,而不能回到另一顆子樹去。就因為這個特殊的vis,now就成了割點。

然後注意細節吧 比如輸出 以及統計答案要單獨掃一遍 因為會有重複的

#include<bits/stdc++.h>
#define N 20005
#define M 100005
using namespace std;
template<class T>
inline void read(T &x)
{
    x=0;
    static char ch=getchar();
    while(ch<'0'||ch>'9')   ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
struct Edge
{
    int to,next,val;
}edge[2*M];
int n,m,tot,first[N];
inline void addedge(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=first[x];
    first[x]=tot;
}
int dfn[N],low[N],sign,cnt;
bool cut[N];
inline void dfs(int now,int fa)
{
    int child=0;
    dfn[now]=low[now]=++sign;
    for(int u=first[now];u;u=edge[u].next)
    {
        int vis=edge[u].to;
        if(vis==fa) continue;
        if(!dfn[vis])
        {
        ++child;
            dfs(vis,now);
            low[now]=min(low[vis],low[now]);
            if(low[vis]>=dfn[now]&&fa!=0) cut[now]=true; //非根節點 
        }
        else low[now]=min(low[now],dfn[vis]);
    }
    if(fa==0&&child>=2) cut[now]=true;  //根節點 
}
int main()
{
    read(n); read(m);
    for(int i=1,u,v;i<=m;i++) read(u),read(v),addedge(u,v),addedge(v,u);
    for(int i=1;i<=n;i++)   if(!dfn[i]) dfs(i,0);
    for(int i=1;i<=n;i++)   if(cut[i])  ++cnt;  //注意要在這裡掃一遍 而不是在dfs裡記錄 因為會重複 
    cout<<cnt<<endl;
    for(int i=1;i<=n;i++)   if(cut[i])  cout<<i<<" ";
    return 0;
}