【洛谷 2341】[HAOI2006]受欢迎的牛

题目描述

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

输入输出格式

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

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

输入输出样例
输入样例#1:
3 3
1 2
2 1
2 3
输出样例#1:
1
说明
只有 3 号奶牛可以做明星

【数据范围】
10%的数据N<=20, M<=50
30%的数据N<=1000,M<=20000
70%的数据N<=5000,M<=50000
100%的数据N<=10000,M<=50000

tarjan暴力缩点,求出度为0的强连通分量的大小。

要格外注意的是,出度为0的强连通分量只能有一个。也就是说,最后的答案是唯一一个出度为0的明星集团的大小。
证明:
①:明星奶牛被所有牛喜欢,出度只能为0。若出度不为0,明星奶牛被所有牛喜欢的同时还喜欢着某个其它奶牛,那么明星奶牛与其它奶牛之间就有一条双向边,又形成一个明星集团,与已经找出的强连通分量冲突;
②:出度为0的强连通分量只能有一个。如果不唯一,还有另一个出度为0的强连通分量,那么不符合“被其他所有牛喜欢”的条件。
因此如果找到多个明星集团,输出0。

:tarjan板子真是打一次错一次。

dfs写错时洛谷会卡一个点。。竟然就一个。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
const int maxn=100000+10;
int first[maxn],nxt[maxn],tot;
int dfn[maxn],low[maxn],scc[maxn],size[maxn],chu[maxn];
int scc_num,dfs_clock;
int n,m,ans;
stack<int>s;
struct edge
{
    int f,t;
}l[maxn];
void build(int f,int t)
{
    l[++tot]=(edge){f,t};
    nxt[tot]=first[f];
    first[f]=tot;
    return;
}
void dfs(int u)
{
    low[u]=dfn[u]=++dfs_clock;
    s.push(u);
    for(int i=first[u];i;i=nxt[i])
    {
        int w=l[i].t;
        if(!dfn[w])
        {
            dfs(w);
            low[u]=min(low[u],low[w]);//
        }
        else if(!scc[w])
        {
            low[u]=min(low[u],dfn[w]);//
        }
    }
    if(low[u]==dfn[u])
    {
        scc_num++;
        while(1)
        {
            int x=s.top();
            s.pop();
            scc[x]=scc_num;
            size[scc_num]++;
            if(x==u) break;
        }
    }
    return;
}
int main()
{
    int f,t;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&f,&t);
        build(f,t);
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i]) dfs(i);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=first[i];j;j=nxt[j])
        {
            int w=l[j].t;
            if(scc[i]!=scc[w]) chu[scc[i]]++;
        }
    }
    int k;
    for(int i=1;i<=scc_num;i++)
    {
        if(chu[i]==0) ans++,k=i;
    }
    if(ans==1) cout<<size[k]<<'\n';
    else puts("0");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值