BZOJ1051 [HAOI2006]受欢迎的牛(强连通分量+缩点)

题目大意:给出一个有向图,求有多少个结点能被任意一个结点到达 


如果图无环,当出度为0的结点只有一个时(受欢迎的牛就是这头),问题有解,否则无解(几个出度为0的结点不会到达对方)

如果图有环,由于同一个强连通分量中的结点相互可达(互相喜欢),它们可以看作一个点(权值为强连通分量的大小)


因此,此题的解法为寻找强连通分量并缩点,之后按无环图处理即可 


一个多月没看,全都忘干净。。。


#include
#include
#include
int u[100005]={0},v[100005]={0},first[10005]={0},next[100005]={0};
int pre[10005]={0},lowlink[10005]={0},sccno[10005]={0},num[10005]={0};//SCC相关 
int s[10005]={0};//栈 
int e=0,clo=0,pscc=0,top=0;
void tj(int x,int y)
{
	u[++e]=x;
	v[e]=y;
	next[e]=first[x];
	first[x]=e;
}
void dfs(int x)
{
	int i;
	lowlink[x]=pre[x]=++clo;
	s[++top]=x;
	for(i=first[x];i!=0;i=next[i])
	{
		if(pre[v[i]]==0)
		{
			dfs(v[i]);
			if(lowlink[x]>lowlink[v[i]]) lowlink[x]=lowlink[v[i]];
		}
		else
			if(sccno[v[i]]==0&&lowlink[x]>lowlink[v[i]]) lowlink[x]=lowlink[v[i]];
	}
	if(lowlink[x]==pre[x])
	{
		pscc++;
		while(1)
		{
			sccno[s[top--]]=pscc;
			num[pscc]++;
			if(s[top+1]==x) break;
		}
	}
}
int main()
{
	int n,m,i,x,y,e2,flag=0,ans;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		tj(x,y);
	}
	for(i=1;i<=n;i++)
		if(pre[i]==0) dfs(i);
	e2=e;//以下为缩点过程 
	e=0;
	memset(first,0,sizeof(first));
	memset(next,0,sizeof(next));
	for(i=1;i<=e2;i++)
		if(sccno[u[i]]!=sccno[v[i]]) tj(sccno[u[i]],sccno[v[i]]);//e恒<=i
	for(i=1;i<=pscc;i++)
		if(first[i]==0)
		{
			flag++;
			ans=num[i];
		}
	if(flag==1) printf("%d",ans);
	else printf("0");
	return 0;
}


你可能感兴趣的:(图的DFS)