1. 程式人生 > >Popular Cows POJ - 2186 (Tarjan求強連通分量 + 縮點)

Popular Cows POJ - 2186 (Tarjan求強連通分量 + 縮點)

Every cow’s dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.

Input

Line 1: Two space-separated integers, N and M
Lines 2…1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.

Output

Line 1: A single integer that is the number of cows who are considered popular
by every other cow.

Sample Input
3 3
1 2
2 1
2 3

Sample Output
1

Hint
Cow 3 is the only cow of high popularity.

題目意思
給出n個節點,m條邊的有向圖,讓你求有多少節點可以從其他任意節點到達。**

思路
先求出所有的強連通分量,然後把每個強連通分量縮成一個點,這樣整個圖就沒有強連通分量了,也就是不存在環。然後找到出度為0的點的個數,只有出度為0的點的個數為1才存在能滿足題意的節點,再計算出這個出度為0的點包含幾個節點答案就是幾。(能滿足提議的收縮之後的點出度確實必須為0,不然的話在其他所有點都能到達它的前提下,如果它出度不為0,那麼意味著它可以到達其他點,構成了環,與圖中不存在環相矛盾。如果出度為0的點的個數大於1的話,那麼意味著這幾個點之間互相不可到達,就不滿足題意了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 5 * 1e4 + 100;

int n, m, top, num, tot, sum;
int head[maxn], Stack[maxn], color[maxn], degree[maxn], dfn[maxn], low[maxn];

struct node
{
    int v, next;
}edge[maxn];

inline void add(int u, int v) 
{
    edge[tot].v = v;
    edge[tot].next = head[u]; 
    head[u] = tot++;
}

inline void Init()
{
    top = 0;
    tot = 0;
    num = 0;
    sum = 0;
    memset(dfn, 0, sizeof(dfn));
    memset(head, -1, sizeof(head));
}
//鏈式前向星存圖以及初始化

void recon(int u) {            //縮點,並統計出度
    for(int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].v;
        if(color[v] != color[u])
        {
            degree[color[u]] ++;
        }
    }
}

void Tarjan(int u)       //求強連通分量
{
    dfn[u] = low[u] = ++num;
    Stack[++top] = u;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        if(!dfn[v])
        {
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(!color[v]) 
	        low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        color[u] = ++ sum;         //給這些節點安排上一個新的編號(縮點之後的編號)
        while(Stack[top] != u)
        {
            color[Stack[top--]] = sum;
        }
        top--;
    }
}


int main() 
{
    //freopen("in.txt", "r", stdin);
    cin >> n >> m;
    Init();
    int u, v;
    for(int i = 1; i <= m; ++ i) 
    {
        scanf("%d%d", &u, &v);
        add(u, v);
    }
    for(int i = 1; i <= n; ++ i)  //Tarjan求強連通分量
    {
        if(!dfn[i])
            Tarjan(i);
    }
    for(int i = 1; i <= n; ++ i)     //縮點,並統計每個點的出度
    {
        recon(i);
    }
    int cnt = 0;
    int id = -1;
    for(int i = 1; i <= sum; ++ i)       //找到出度為零的點的個數,並記錄下其節點編號(縮點後的編號)
    {
        if(degree[i] == 0)
        {
            cnt ++;
            id = i;
        }
    }
    int ans = 0;
    if(cnt == 1)             //如果出度為零的點只有一個那麼答案就是這一個強連通分量中的所有點
    {
        for(int i = 1;i <= n; ++ i) 
        {
            if(color[i] == id)
                ans++;
        }
    }
    printf("%d\n", ans);
    return 0;
}