AcWing1174 受欢迎的牛(tarjan缩点)

对于有向图问题,如果能转化成dag那么就会好做很多,因为这题如果是dag,那么只要只存在一格出度为0的,就是答案

而强连通分量中的点都可以互达,所以进行tarjan缩点。

tarjan缩点的原理,分为四个边,树边,前向边,后向边,横叉边

dfn是时间戳,tarjan的栈中存的是祖先和已被遍历的并且能够走到当前节点祖先或者本身的点,以及本身。

而low数组存的是只经过一条后向边或者横叉边可以走到的dfn最小的并且能够走到当前节点祖先或者本身的点,也就是栈中的点

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int id[N];
int h[N],ne[N],w[N],e[N],idx;
stack<int> q;
int scnt;
int cnt[N];
int times;
int n,m;
int dfn[N],low[N];
int ins[N];
int in[N];
void tarjan(int u){
    dfn[u]=low[u]=++times;
    int i;
    q.push(u),ins[u]=1;
    for(i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(!dfn[j]){
            tarjan(j);
            low[u]=min(low[u],low[j]);
        }
        else if(ins[j]){
           low[u]=min(low[u],dfn[j]); 
        }
        
    }
    if(dfn[u]==low[u]){
        ++scnt;
        while(1){
            int t=q.top();
            q.pop();
            ins[t]=0;
            cnt[scnt]++;
            id[t]=scnt;
            if(t==u)
            break;
        }
    }
}
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int main(){
    cin>>n>>m;
    int i;
    memset(h,-1,sizeof h);
    for(i=1;i<=m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    for(i=1;i<=n;i++){
        if(!dfn[i])
            tarjan(i);
    }
    int j;
    for(i=1;i<=n;i++){
        for(j=h[i];j!=-1;j=ne[j]){
            int k=e[j];
            if(id[i]!=id[k]){
                in[id[i]]++;
            }
        }
    }
    int sum=0;
    int sign=0;
    for(i=1;i<=scnt;i++){
        if(!in[i]){
            sum+=cnt[i];
            sign++;
        }
        if(sign>1){
            sum=0;
            break;
        }
    }
    cout<<sum<<endl;
}
View Code

 

posted @ 2020-05-21 23:02  朝暮不思  阅读(162)  评论(0编辑  收藏  举报