zoukankan      html  css  js  c++  java
  • 【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
    ",ans);
        return 0;
    }
     
     
     
  • 相关阅读:
    卷积神经网络之ResNet网络模型学习
    进程调度
    进程基础知识
    顺序栈链栈
    图的基本概念和术语
    关系数据库SQL复习
    关系数据库域关系演算语言QBE
    关系数据库元组关系演算语言ALPHA
    关系数据库关系模型、数据结构、完整性、关系代数
    数据库数据模型和系统结构
  • 原文地址:https://www.cnblogs.com/hxer/p/5186325.html
Copyright © 2011-2022 走看看