【BZOJ】1051: [HAOI2006]受欢迎的牛

[HAOI2006]受欢迎的牛

Description

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

Input

第一行两个数N,M。 接下来M行,每行两个数A,B,意思是A认为B是受欢迎的(给出的信息有可能重复,即有可能出现多个A,B)

Output

一个数,即有多少头牛被所有的牛认为是受欢迎的。

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

HINT

100%的数据N<=10000,M<=50000
 
思路:开始认为可以使用并查集,但是由于给个点有多个父节点,朴素的并查集不行;
这样就需要建图,使用Tarjan对强连通分量进行缩点,之后以缩点作为并查集的元素(只是思想,因为答案要求的只是一部分,所以只看出度即可),看是否只有一个根节点(每一个缩点是环,只要有一个点有一条指向另一个缩点的有向边,那么该缩点中所有的元素都是所指向缩点中点的粉丝);这样只需说明只有一个点出度为0;即只有一个根节点即可;那么该根节点所代表的缩点中点数就是answer;
 
ps:开始认为Tarjan之后对每条边进行遍历;(受hdu2767 Proving Equivalences添边构造强连通图的影响)当边为两个连通分量(缩点)的连边时,就将缩点所代表的点个数进行累加,之后对所有点代入到所在的联通分量(belong)中,看是否集合大小为n(包含了自己);但是这种思想是错误的。因为分析的时候只是看了单独的两个缩点之间的关系;即使使用set排重,但是只是两个缩点之间可以实现排重。这样如果扩展到三个缩点就会发现会出现重复计算;
/********************************************************
    Time:88 ms
    Memory:2888 kb
****************************************************************/

#include<iostream>
#include<cstdio>
#include<stack>
#include<cstring>
using namespace std;
#define rep0(i,l,r) for(int i = (l);i < (r);i++)
#define rep1(i,l,r) for(int i = (l);i <= (r);i++)
#define rep_0(i,r,l) for(int i = (r);i > (l);i--)
#define rep_1(i,r,l) for(int i = (r);i >= (l);i--)
#define MS0(a) memset(a,0,sizeof(a))
#define MS1(a) memset(a,-1,sizeof(a))
const int MAXN = 50050;
int head[MAXN],tot;
struct edge{
    int to,w,Next;
}e[MAXN];
void ins(int a,int b,int w = 0)
{
    e[++tot].Next = head[a];
    e[tot].to = b;
    e[tot].w = w;
    head[a] = tot;
}
const int N = 10020;
int pre[N],dfs_clock,low[N];
int belong[N],scc,num[N];
stack<int> S;
bool stk[N];
void Tarjan(int u)
{
    pre[u] = low[u] = ++dfs_clock;
    S.push(u);
    stk[u] = true;
    int v;//点u所在连通分量的出度;
    for(int i = head[u];i;i = e[i].Next){
        v = e[i].to;
        if(pre[v] == 0){
            Tarjan(v);
            low[u] = min(low[u],low[v]);
        }else if(stk[v]){
            low[u] = min(low[u],pre[v]);
        }
    }
    if(pre[u] == low[u]){//强连通分量的根节点
        ++scc;
        do{
            v = S.top();
            S.pop();stk[v] = false;
            belong[v] = scc;
            num[scc]++;
        }while(v != u);
    }
}
int id[MAXN];
int main()
{
    int T,kase = 1;
    MS0(head);tot = 0;
    int n,m,a,b;
    scanf("%d%d",&n,&m);
    rep0(i,0,m){
        scanf("%d%d",&a,&b);
        ins(a,b);
    }
    scc = dfs_clock = 0;
    rep1(i,0,n) pre[i] = low[i] = num[i] = belong[i] = 0;
    rep1(i,1,n)if(pre[i] == 0)
        Tarjan(i);
    int ans = 0;MS0(id);
    rep1(u,1,n){
        for(int index = head[u];index;index = e[index].Next){
            int v = e[index].to;
            int bv = belong[v],bu = belong[u];
            if(bv != bu && id[bu] == 0){//***强连通分量之间的连边
               id[bu]++;//标记出度不为0的点
            }
        }
    }
    rep1(i,1,scc)
        if(id[i] == 0){
            if(ans) return puts("0"),0;
            ans = num[i];
        }
    printf("%d\n",ans);
    return 0;
}

 

 
 
 
posted @ 2016-02-11 10:16  hxer  阅读(...)  评论(... 编辑 收藏