題解 P2341 【[USACO03FALL][HAOI2006]受歡迎的牛 G】

每頭奶牛都夢想成爲牛棚裏的明星。被所有奶牛喜歡的奶牛就是一頭明星奶牛。所有奶牛都是自戀狂,每頭奶牛總是喜歡自己的。奶牛之間的“喜歡”是可以傳遞的——如果 AA 喜歡 BBBB 喜歡 CC,那麼 AA 也喜歡 CC。牛欄裏共有 NN 頭奶牛,給定一些奶牛之間的愛慕關係,請你算出有多少頭奶牛可以當明星。

終於AC了!

在這裏插入圖片描述

前置知識

做這道題需要知道 強聯通分量 不會的可以學一下。

分析

我們發現所以受歡迎的牛一定是一個強聯通分量(假設受歡迎的牛是存在的)

兩個強聯通分量可以會有橫叉邊使一個強聯通分量可以到達另一個強聯通分量。

這是我們可以把每個強聯通分量看成點(這個就是所謂的縮點),就變成了一個普通的有向圖。

我們發現當只有一個點的出度都 =0=0 的話,答案就是那個點了

否則答案就是 00

爲什麼?

首先這個圖不會出現環,不然就不滿足了強聯通分量的性質,強聯通分量是有向圖的極大強連通子圖(極大的意思是不能再加點),那這顯然說明了這個圖無環。

這個圖無環有說明什麼?

無環就說明這張圖不連通,不連通又說明什麼?

說明出度爲 00 的點至少有 11 個。

爲什麼出度爲 00 的點 >1> 1 答案就是 00 了呢?

很顯然:

  • 如果出度爲 00 的點 >1> 1 ,肯定沒有點能成爲明星。

  • 如果出度爲 00 的點 =1= 1 ,那個出度爲 00 的點肯定就是明星。

代碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
template<typename T>inline void write(T x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar('0'+x%10);
}
const int MAXN=1e6+10,MAXM=1e6+10;
int s[MAXN], stop,dfn[MAXN],low[MAXN],scccnt, sccnum[MAXN],dfscnt,tot,he[MAXN],ne[MAXM<<1],ed[MAXM<<1],n,x,y,sz[MAXN],de[MAXN],ans,m;
void add(int x,int y){
	ed[++tot]=y;
	ne[tot]=he[x];
	he[x]=tot;
}
inline void tarjan(int now){
	dfn[now] = low[now] = ++dfscnt;
	s[stop++] = now;
	for (int i = he[now]; i; i = ne[i]){
		if(!dfn[ed[i]]){
			tarjan(ed[i]);
			low[now] = min(low[now], low[ed[i]]);
		}else if(!sccnum[ed[i]]) {
			low[now] = min(low[now], dfn[ed[i]]);
		}
	}

	if (dfn[now]==low[now]){
		scccnt++;
		do {
			sccnum[s[--stop]]=scccnt;
			sz[scccnt]++;
		}while(s[stop]!=now);
	}
}
int main(){
	read(n);read(m);
	for(int i=1;i<=m;i++){
		read(x);read(y);
		add(x,y);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i])tarjan(i);
	for(int i=1;i<=n;i++)
		for(int j=he[i];j;j=ne[j])
			if(sccnum[i]!=sccnum[ed[j]])de[sccnum[i]]++;//記錄出度
	for(int i=1;i<=scccnt;i++)
		if(!de[i]){//出度爲0
			if(ans){
				cout<<0;//出度爲0的點>1,答案爲0
				return 0;
			}
			ans=i;
		}
	cout<<sz[ans];//否則就是這個點
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章