码迷,mamicode.com
首页 > 其他好文 > 详细

[HAOI2006] 受欢迎的牛

时间:2018-04-01 21:49:59      阅读:143      评论:0      收藏:0      [点我收藏+]

标签:div   add   mat   过程   tarjan算法   条件   post   反向   oid   

  首先对于一个强联通分量内的所有牛来说,他们彼此都认为对方受欢迎,且对于这个强联通分量内的牛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] 受欢迎的牛

标签:div   add   mat   过程   tarjan算法   条件   post   反向   oid   

原文地址:https://www.cnblogs.com/qjs12/p/8688079.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!