1. 程式人生 > >tarjan 受歡迎的牛

tarjan 受歡迎的牛

https://loj.ac/problem/10091

既然愛慕關係可以傳遞,那麼將互相可達的某幾個點縮成一個點,就能簡化原圖了,

那麼很自然想到tarjan求強連通分量

那麼就只需找到可以被所有縮點遍歷到的那個縮點

再輸出它所含的點數就行了

先用tarjan將所有聯通分量進行縮點,縮點後考慮出度為0的點的個數:

(1)個數大於1的時候,顯然不存在受歡迎的牛!

(2)個數等於0的時候,假設有一頭牛X是受歡迎的,那麼它必定有喜歡的牛Y,而它又收到牛Y的歡迎,說明存在環,不是DAG圖,矛盾!

(3)個數等於1的時候,用數學歸納法推一下,有這樣一個結論:出度為0的那個點可以被其它所有點到達

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
const int maxm=5e4+5;
int n, m, a, b, cnt, num, top, col;
int head[maxm];
int s[maxm];
int st[maxm]; 
int co[maxm];
int de[maxm];
int dfn[maxm], low[maxm];
using namespace std;
struct cow {
	int to;
	int next;
} edge[maxm];
void add(int x, int y) {
	cnt++;
	edge[cnt].to = y;
	edge[cnt].next = head[x];
	head[x] = cnt;
}
void tarjan(int u) {
	dfn[u] = low[u] = ++num;
	st[++top] = u;
	for (int i = head[u]; i; i = edge[i].next) {
		int v = edge[i].to;
		if (dfn[v] == 0) {
			tarjan(v);
			low[u] = min (low[u], low[v]);
		} else if(co[v] == 0) 
		    low[u] = min (low[u], low[v]);
	}
	if (low[u] == dfn[u]) {
		co[u] = ++col;
		++s[col];
		while (st[top] != u) {
			++s[col];
			co[st[top]] = col;
			--top;
		}
		--top;
	}
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &a, &b);
		add(b, a); //倒著存以最後統計出度而不是統計入度
	}
	for (int i = 1; i <= n; i++) 
		if (!dfn[i]) 
		    tarjan (i);
	for (int i = 1; i <=n; i++) 
	    for (int j = head[i]; j; j = edge[j].next) {
	    	int v = edge[j].to;
	    	if (co[i] != co[v]) 
			    de[co[v]]++;
		}
	int ans = 0, u = 0;
	for (int i = 1; i <= col; i++) 
	    if(de[i] == 0) {
	    	ans = s[i];
	    	u++;
		}
	if(u == 1) 
	   printf("%d\n", ans);
	else  
       printf("0\n");
	return 0;
}
void tarjan(int 當前點)
{
    這個點的low=dfn=時間戳;
    將這個點入棧;
    標記這個點入棧;
    列舉這個點連線的所有邊
    {
        如果目標點沒有被訪問過
        {
            tarjan(目標點);
            更新當前點的low; 
        }  
        如果目標點被訪問過
        {
            更新當前點的low; 
        } 
    }
    如果當前點的low==dfn
    {
        將這個點及棧以上的點出棧,標記成一個強連通分量; 
        ans++; 
    } 
}