【题解】HAOI-2006 受欢迎的牛

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶

牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果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

题目概要

给出一张有向图,求出这样的结点的个数:这种结点满足从图中任意一点出发均可到达该点

思路

看一看数据范围, O(n2) O ( n 2 ) 的暴力是肯定不可以的,再一想,一个强连通分量可以互达,而题目中只要求判断到达与否,而非权值

所以自然地使用到强连通分量缩点,本人偏爱使用tarjan (因为无脑)

至于这题的缩点,由于只要求判断到达与否,用一个映射表表示缩点后重新建图的对应点,最后遍历一遍边,求出所有点的出度(指向自己的边忽略)

至于为什么只用求出度,可以感性地想一想,缩点后的图中不可能有两个点及以上的强连通分量(因为这样的话,它们就会被缩成一个点)

那么若没有点有出度,图就是一个点,或几个点,输出n或0

若出度为0的点的个数大于1,那么指向不可能集中到一只牛身上,输出0

若只有一个点出度为零,那么明星就是这个点的扩展点数量

顺带拿这题练了练对拍

感谢同机房dalao cgz 对Ubuntu下对拍的大力支持

极丑的代码

#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))

template <typename _Tp> inline void read(_Tp &x){
    char c11=getchar();x=0;bool booo=0;
    while(c11<'0'||c11>'9'){if(c11=='-')booo=1;c11=getchar();}
    while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}
    if(booo)x=-x;
    return ;
}

const int maxn=10050,maxm=50050;
struct node {int v,nxt;} a[maxm];
int head[maxn],p=0;
int low[maxn],dfn[maxn];
bool vis[maxn];
int num__=0;
int sta[maxn],top=0;
int colour=0;
int leaf[maxn];
int A[maxm],B[maxm];
int tot[maxn];
struct node1 {int v,nxt;} b[maxm];
int head1[maxn],pp=0;
int es[maxn];
int n,m;

inline void app(int x,int y);
inline void add(int u,int v);
void init();
void again();

void tarjan(int x){
    low[x]=dfn[x]=++num__;
    vis[x]=1;
    sta[++top]=x;
    for(int i=head[x];i;i=a[i].nxt){
        int v=a[i].v;
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(vis[v])low[x]=min(low[x],dfn[v]);
    }
    if(dfn[x]==low[x]){
        vis[x]=0;
        leaf[x]=++colour;
        es[colour]++;
        for(;sta[top]!=x;top--){
            leaf[sta[top]]=colour;
            vis[sta[top]]=0;
            es[colour]++;
        }
        top--;
    }
    return ;
}

void work(){
    int temp=0,tem;
    for(int i=1;i<=colour;i++)
        if(!tot[i]){
            temp++;
            tem=i;
        }
    if(temp!=1)putchar('0');
    else printf("%d",es[tem]);
    return ;
}

int main(){
    init();
    for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
    again();
    work();
    return 0;
}

void init(){
    cl(head);cl(low);cl(dfn);cl(vis);cl(tot);cl(es);
    read(n);read(m);
    for(int i=1;i<=m;i++){
        read(A[i]);read(B[i]);
        add(A[i],B[i]);
    }
    return ;
}

inline void app(int x,int y){
    if(leaf[x]==leaf[y])return ;
    b[++pp].v=leaf[y];
    b[pp].nxt=head1[leaf[x]];
    head1[leaf[x]]=leaf[y];
    tot[leaf[x]]++;
    return ;
}

void again(){
    for(int i=1;i<=m;i++){
        app(A[i],B[i]);
    }
    return ;
}

inline void add(int u,int v){
    a[++p].v=v;
    a[p].nxt=head[u];
    head[u]=p;
    return ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值