[HAOI2006]受欢迎的牛——[Tarjan缩点]

【题目描述】

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

【输入格式】
 第一行:两个用空格分开的整数:N和M
 第二行到第M + 1行:每行两个用空格分开的整数:A和B,表示A喜欢B

【输出格式】
 一行:单独一个整数,表示明星奶牛的数量

【样例输入】
3 3
1 2
2 1
2 3

【样例输出】
1

【题意分析】
给你一堆有向边,求一个整体没有出度的强联通分量内的节点个数

可能比较难理解,那我们来看看样例:共有2个强联通分量:{1,2} {3}

这两个强联通分量由“2 3”这条有向边相连接,表示2爱慕3,那么{1,2}这个强联通分量整体就有出度了(它连接到了强联通分量{3}),因此不符合要求,所以符合要求的整体没有出度的强联通分量就是{3},节点个数1即为答案。

但是我们会碰到这样一个情况:所有的奶牛喜欢来,喜欢去,构成了一个包括所有节点的环,这种情况只要在统计的时候打个布尔值标记,判断当前有没有过出度为0的强联通分量,如果这种情况来了两次,那么整体就是一个环,直接输出0.。

Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAX 60000
using namespace std;

struct Front_Link_Star{   //链式前向星
    int next,to;
}edge[MAX];

int head[MAX],dfn[MAX],low[MAX],Stack[MAX],size[MAX];
int DAG[MAX],degree[MAX],n,m,tag,top,cnt,tot_circle;
bool vis[MAX];

inline void Add_Edge(int u,int v){
    edge[++cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
}

inline int read(){
    int s=0,w=1;
    char ch=getchar();
    while (!isdigit(ch)){if (ch=='-')w=-1;ch=getchar();}
    while (isdigit(ch)){s=(s << 3)+(s << 1)+ch-'0';ch=getchar();}
    return s*w;
}

inline void tarjan(int now){  //缩点
    dfn[now]=low[now]=++tag;
    vis[now]=1;Stack[++top]=now;
    for (register int i=head[now];i;i=edge[i].next){
        int v=edge[i].to;
        if (!dfn[v]){
            tarjan(v);
            low[now]=min(low[now],low[v]);
        }else if (vis[v])low[now]=min(low[now],low[v]);
    }
    if (dfn[now]==low[now]){
        ++tot_circle;
        while (int y=Stack[top--]){
            DAG[y]=tot_circle;   
            //DAG[]记录的是所属的强联通分量编号
            vis[y]=0;
            ++size[tot_circle];
            //size[]记录的是强联通分量的大小
            if (now==y)break;
        }
    }
}

int main(){
    n=read(),m=read();
    for (register int i=1;i<=m;i++){
        int x=read(),y=read();
        Add_Edge(x,y);
    }
    for (register int i=1;i<=n;i++)
        if (!dfn[i])tarjan(i);
    for (register int i=1;i<=n;i++){  //这里是遍历全图,并统计出度
        for (register int j=head[i];j;j=edge[j].next){
            int v=edge[j].to;
            if (DAG[i]!=DAG[v])++degree[DAG[i]];
            //degree[]记录每个强联通分量的“整体出度”
        }
    }
    int ans=0;bool flag;
    for (register int i=1;i<=tot_circle;i++)
        if (!degree[i]){
            if (flag){   //这种情况爆了两次
                ans=0;
                printf("%d",ans);
                return 0;
            }
            flag=1;ans=size[i];   //size[i]即为答案
        }
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mega_Aspirin/article/details/81435921
今日推荐