1. 程式人生 > >POJ2533&&SP1799 The Bottom of a Graph(tarjan+縮點)

POJ2533&&SP1799 The Bottom of a Graph(tarjan+縮點)

POJ2553

SP1799


我們知道單獨一個強連通分量中的所有點是滿足題目要求的

但如果它連出去到了其他點那裡,要麼成為新的強連通分量,要麼失去原有的符合題目要求的性質

所以只需tarjan縮點求出所有強連通分量,再O(E)列舉所有邊,是否會成為連線一個分量與另一個分量的邊——即一條出度——即可

如果一個分量沒有出度,那麼他中間的所有點都是符合題目要求的點


(因為快讀快輸加了太長所以就不貼了)

const int N=5005,M=N*N>>1;
int h[N],en,n,m,dfn[N],out[N],bel[N],low[N],num,cnt;
stack<int> st;
struct edge{int n,u,v;}e[M]; //前向星存邊
inline void add(const int &x,const int &y){e[++en]=(edge){h[x],x,y},h[x]=en;}
inline void tarjan(int x){ //一個tarjan縮點STL棧模板
    st.push(x);
    dfn[x]=low[x]=++num;
    for(int i=h[x];i;i=e[i].n){
        int y=e[i].v;
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(!bel[y])
            low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x]){
        cnt++;
        int TOP;
        do{
            TOP=st.top();
            st.pop();
            bel[TOP]=cnt;
        }while(TOP!=x);
    }
}
signed main(){
    read(n);
    while(n){
        en=num=cnt=0;
        memset(h,0,sizeof h);
        memset(dfn,0,sizeof dfn);
        memset(out,0,sizeof out);
        memset(bel,0,sizeof bel);
        memset(low,0,sizeof low);
        read(m);
        while(m--){
            int x,y;
            read(x),read(y);
            add(x,y);
        }
        for(int i=1;i<=n;i++) if(!dfn[i]) //跑縮點
            tarjan(i);
        for(int i=1,u,v;i<=en;i++){
            u=e[i].u,v=e[i].v;
            if(bel[u]!=bel[v]) out[bel[u]]++; //判斷每條邊的起點終點是否在同一強連通分量中,如果不是,則起點所在強連通分量出度加1
        }
        for(int i=1;i<=n;i++) if(!out[bel[i]]) //如果點i所在強連通分量沒有出度則滿足要求,輸出
            Write(i,' ');
        printf("\n"); //統一換行
        read(n);
    }
}