【2186】Popular Cows(强连通分支及其缩点)

【2186】Popular Cows(强联通分支及其缩点)


Popular Cows
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 28323 Accepted: 11459

Description

Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.

Input

* Line 1: Two space-separated integers, N and M

* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.

Output

* Line 1: A single integer that is the number of cows who are considered popular by every other cow.

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

Hint

Cow 3 is the only cow of high popularity.

Source


题目大意是有n头牛,他们有m对特殊的关系

关系A B表示A仰慕B 恩 是感觉怪怪的= =

然后如果A仰慕B B仰慕C 那么A也仰慕C 也就是关系是可传递的

要求找出被其他牛都仰慕的牛的数目


一开始很费解,要不是放在强连通里,实在没法往这方向靠……

其实反过来想的话比较好像,强连通分支的定义——强连通分支中从任何一个点都可以访问到其余各点(有向图)

这样再回到题中 可以得出两个结论

1.如果某个强联通分支中的某只牛被分支外的所有牛都仰慕,也就是说分支外的牛都有一条通向他的路,和他在同一个强连通分支里的所有牛也是满足要求的。

2.如果分支中有某头牛仰慕分支外的牛,那么就不存在被所有牛都仰慕的牛。(可以换种方式来想,如果分支中某头牛仰慕分支外的牛,还被其余牛都仰慕,那么分支外的这头牛也应该被包含在分支内。因为这样就说明分支外的那头牛,会有一条通向分支内的路径,也就是符合强连通分量的定义)

3.经2结论可知,出度为0的强连通分支,就是满足条件的牛群。但如果有两群,就不存在这种牛。因为两个分支间是没有关系的。可以自己画一画看看。


找到方法判断就好办了,首先把所有的强连通分支求出来,缩点后变成一团团的。找出出度为0的缩点。如果存在两个或两个以上,答案就是0。如果之存在一个,那么这个点中的所有牛都是满足题意的牛,统计输出即可。


代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#include <stack>
#define LL long long
#define fread() freopen("in.in","r",stdin)
#define fwrite() freopen("out.out","w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const double eps = 1e-8;

struct Edge
{
	int v,next;
};

Edge eg[50050];
int head[10010];
int vis[10010];
//dfs序
int dfn[10010];
//最早可访问到的点
int low[10010];
//缩点的出度
int in[10010];
//缩点是否满足出度为0
bool can[10010];
//缩点的根
int pre[10010];
int n,tp,tm;
stack <int> s;

void tarjan(int u)
{
	s.push(u);
	vis[u] = 1;
	low[u] = dfn[u] = tm++;
	int v;

	for(int i = head[u]; i != -1; i = eg[i].next)
	{
		v = eg[i].v;
		//点未被访问过
		if(vis[v] == 0)
		{
			tarjan(v);
			vis[v] = 1;
			low[u] = min(low[u],low[v]);
		}
		//点在栈中
		else if(vis[v] == 1)
		{
			low[u] = min(low[u],dfn[v]);
		}
	}

	//此点为树根
	if(low[u] == dfn[u])
	{
		int x = s.top();
		while(x != u)
		{
			pre[x] = u;
			s.pop();
			x = s.top();
		}
		//标记该分支中各点树根为u
		pre[x] = u;
		s.pop();
	}
}

int main()
{
	int u,v;
	while(~scanf("%d%d",&n,&tp))
	{
		memset(head,-1,sizeof(head));

		for(int i = 0; i < tp; ++i)
		{
			scanf("%d%d",&u,&v);
			eg[i].v = v;
			eg[i].next = head[u];
			head[u] = i;
		}

		memset(vis,0,sizeof(vis));

		for(int i = 1; i <= n; ++i)
		{
			if(vis[i]) continue;

			tm = 0;
			tarjan(i);
		}


		memset(in,0,sizeof(in));
		memset(can,0,sizeof(can));
		int f = 0;
		for(int i = 1; i <= n; ++i)
		{
			for(int j = head[i]; j != -1; j = eg[j].next)
			{
				v = eg[j].v;
				//两个点不在同一个分支内
				if(pre[v] != pre[i])
				{
					in[i]++;
				}
			}
			//该点出度不为0
			if(in[i])
			{
				//该缩点出度不为0
				can[pre[i]] = 1;
			}
		}

		int cnt = 0;
		for(int i = 1; i <= n; ++i)
		{
			//该点为分支根,同时该分支出度为0
			if(pre[i] == i && can[i] == 0) 
			{
				f = i;
				cnt++;
			}
			if(cnt > 1) break;
		}
		
		//出度为0的分支多余1个
		if(cnt != 1)
		{
			puts("0");
		}
		else
		{
			cnt = 0;
			//统计分支中的点数
			for(int i = 1; i <= n; ++i)
			{
				if(pre[i] == f) cnt++;
			}
			printf("%d\n",cnt);
		}
	}

	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值