1. 程式人生 > >[HAOI2006] 受歡迎的牛

[HAOI2006] 受歡迎的牛

AR bsp edge microsoft 10000+ 接下來 main body 一個點

首先對於一個強聯通分量內的所有牛來說,他們彼此都認為對方受歡迎,且對於這個強聯通分量內的牛A來說,假設它認為不在這個強連通分量內的一頭牛B是受歡迎的,那麽這個強聯通分量內的所有牛都認為牛B受歡迎。

我們用Tarjan算法求一遍SCC,把一個SCC縮成一個點,並添加連接不同SCC的邊,註意這條邊是一條反向邊,本來的邊由a->b,我們要添加的這條邊由scc[b]->scc[a],這樣做是為了方便之後的DFS,最後得到一個DAG。

接下來我們在這個DAG上從所有入度為0的scc開始DFS,並記錄DFS過程中訪問到的SCC個數記為cnt,如果DFS結束後cnt==scc_cnt,代表所有SCC都認為這個SCC是受歡迎的,則所有牛都認為這個SCC內的牛是受歡迎的,累計牛的個數進答案即可。

我們只需要從入度為0的SCC開始DFS,是因為假設有一條邊SCC_A->SCC_B,代表SCC_B認為SCC_A是受歡迎的,又因為圖是DAG,所以SCC_A一定不認為SCC_B是受歡迎的,那麽SCC_B一定無法達到被所有牛認為是受歡迎的條件,所以我們證明了入度不為0的SCC一定不會是解。

// q.c

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
const int M=10000+10;
int n,m,ans,cnt1,cnt2,dc,scc_cnt,scc[M],h1[M],h2[M],w[M],pre[M],low[M],in[M],out[M];
bool vis[M];
stack<int> s;
struct Edge {
	int u,v,next;
	Edge():u(0),v(0),next(-1) {}
}ed1[M*5],ed2[M*5];
void add_edge(int a,int b,bool flag) {
	if(flag) ed1[++cnt1].u=a,ed1[cnt1].v=b,ed1[cnt1].next=h1[a],h1[a]=cnt1;
	else in[b]++,out[a]++,ed2[++cnt2].u=a,ed2[cnt2].v=b,ed2[cnt2].next=h2[a],h2[a]=cnt2;
}
void dfs1(int u) {
	pre[u]=low[u]=++dc;
	s.push(u);
	for(int i=h1[u];i!=-1;i=ed1[i].next) {
		Edge p=ed1[i];
		if(!pre[p.v]) {
			dfs1(p.v);
			low[u]=min(low[u],low[p.v]);
		} else if(!scc[p.v]) 
			low[u]=min(low[u],low[p.v]);
	}
	if(low[u]==pre[u]) {
		++scc_cnt;
		for(;;) {
			int x=s.top(); s.pop();
			scc[x]=scc_cnt;
			if(x==u) break;
		}
	}
}
void dfs2(int u,int &d) {
	vis[u]=true; ++d;
	for(int i=h2[u];i!=-1;i=ed2[i].next) 
		if(!vis[ed2[i].v]) dfs2(ed2[i].v,d);
}
int main() {
	freopen("cow.in","r",stdin);
	freopen("cow.out","w",stdout);
	memset(h1,-1,sizeof(h1));
	memset(h2,-1,sizeof(h2));
	scanf("%d%d",&n,&m);
	int a,b,tot;
	for(int i=1;i<=m;i++) {
		scanf("%d%d",&a,&b);
		add_edge(a,b,1);
	}
	for(int i=1;i<=n;i++) 
		if(!pre[i]) dfs1(i);
	for(int i=1;i<=n;i++) w[scc[i]]++;
	for(int i=1;i<=cnt1;i++) {
		Edge p=ed1[i];
		if(scc[p.u]!=scc[p.v]) add_edge(scc[p.v],scc[p.u],0);
	}
	for(int i=1;i<=scc_cnt;i++)
		if(!in[i]) {
			memset(vis,0,sizeof(vis));
			dfs2(i,tot=0);
			if(tot==scc_cnt) ans+=w[i];
		}
	printf("%d\n",ans);
	return 0;
}

[HAOI2006] 受歡迎的牛