Bzoj1051 受欢迎的牛

每一头牛的愿望就是变成一头最受欢迎的牛。现在有 N 头牛,给你 M 对整数 (A,B),表示牛 A 认为牛 B 受欢迎。这种关系是具有传递性的,如果 A 认为 B 受欢迎,B 认为 C 受欢迎,那么牛 A 也认为牛 C 受欢迎。你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的


第一眼是个很弱智的Tarjan缩点,然后判断有没有连通分量的入度为连通分量个数减一

但是我把传递性想的太简单了

如果有下图这样的,我就只会判定出3号点有一个入度,但是正确值为2

所以我们换一个角度,从缩点的性质来考虑

我们知道,缩点之后的图是一个DAG(有向无环图)

所以一个节点如果有出度,就不可能被它所到的点崇拜,否则就有环了

所以我们得出了第一条结论,只有出度为零的点才能被所有点崇拜

然后有的人会问如果有多个节点的出度为零怎么办呢

即使不从图的角度来看,这两个出度为零的点也是不可能互相崇拜的,所以不成立

下面给出代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cmath>
using namespace std;
inline int rd(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
    return ;
}
int n,m;
int head[1000006],nxt[1000006],to[1000006];
int total;
void add(int x,int y){
    total++;
    to[total]=y;
    nxt[total]=head[x];
    head[x]=total;
    return ;
}
int dfn[1000006];
int low[1000006];
int tot=0;
int book[1000006];
int sta[1000006];
int set=0;
int v[1000006];
int cnt=0;
int color[1000006];
void tarjan(int x){
    low[x]=dfn[x]=++tot;
    sta[++set]=x;
    book[x]=1;
    for(int e=head[x];e;e=nxt[e]){
        if(!dfn[to[e]]){
            tarjan(to[e]);
            low[x]=min(low[x],low[to[e]]);
        }
        else if(book[to[e]]) low[x]=min(low[x],dfn[to[e]]);
    }
    if(dfn[x]==low[x]){
        cnt++;
        book[x]=0;
        v[cnt]++;
        color[x]=cnt;
        while(set&&sta[set]!=x){
            book[sta[set]]=0;
            v[cnt]++;
            color[sta[set]]=cnt;
            set--;
        }
        set--;
    }
    return ;
}
int du[1000006];
int main(){
    n=rd(),m=rd();
    for(int i=1;i<=m;i++){
        int x=rd(),y=rd();
        add(x,y);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    int ans=0;
    for(int i=1;i<=n;i++){
        for(int e=head[i];e;e=nxt[e]){
            if(color[i]!=color[to[e]]){
                du[color[i]]++;
            }
        }
    }
    int num=0;
    for(int i=1;i<=cnt;i++){
        if(du[i]==0){
            num++;
            ans+=v[i];
        }
    }
    if(num==1) write(ans);
    else write(0);
    return 0;
}

 

posted @ 2018-10-27 16:14  SPRY_NYSK  阅读(...)  评论(... 编辑 收藏