1. 程式人生 > >P2341 [HAOI2006]受歡迎的牛(tarjan + 縮點 )

P2341 [HAOI2006]受歡迎的牛(tarjan + 縮點 )

題目背景

本題測試資料已修復。

題目描述

每頭奶牛都夢想成為牛棚裡的明星。被所有奶牛喜歡的奶牛就是一頭明星奶牛。所有奶

牛都是自戀狂,每頭奶牛總是喜歡自己的。奶牛之間的“喜歡”是可以傳遞的——如果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進行縮點,然後圖就變成了一個有向無環圖,那麼我們直接在這個DAG圖中找出出度為0的點,該點就是我們要求的點,如果這種點存在倆個,說明不存在所有的牛都崇拜的牛,直接輸出0,如果存在一個,那麼考慮到這個點可能是縮點以後的點,所以我們只要輸出該縮點內部的點便可以了,具體見程式碼

#include<iostream>
using namespace std;
const int maxn = 50010;
int head[maxn], num[maxn], dfn[maxn], low[maxn], Stack[maxn], vis[maxn], ans[maxn], result[maxn], belong[maxn];
int n, m, cnt = 1, a, b, total, sum = 0, index;
struct node {
    int to;
    int next;
}edge[2 * maxn];

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

void tarjan(int u) {
    dfn[u] = low[u] = ++total;
    vis[u] = 1;
    Stack[++index] = u;
    for (int i = head[u]; i != 0; i = edge[i].next) {
        int v = edge[i].to;
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if (vis[v]){
            low[u] = min(low[u], dfn[v]);
        }
    }
    if (low[u] == dfn[u]) {
        sum++;
        do {
            int p = Stack[index--];
            vis[p] = 0;
            belong[p] = sum;
        } while (Stack[index + 1] != u);
    }
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        cin >> a >> b;
        add(a, b);
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) {
            tarjan(i);
        }
    }

    for (int i = 1; i <= n; i++) {
        ans[belong[i]]++;
        for (int j = head[i]; j != 0; j = edge[j].next) {
            int v = edge[j].to;
            if (belong[i] != belong[v]) result[belong[i]]++;//這一步是用來判斷縮點的出度的
        }
    }
    int num = 0;
    for (int i = 1; i <= sum; i++) {
        if (!result[i]) {
            if (num) {
                cout << 0 << endl;
                return 0;
            }
            num = i;
        }
    }
    cout << ans[num] << endl;
    return 0;
}