Popular Cows受欢迎的牛--POJ2186--tarjan缩点

Time Limit: 2000MS Memory Limit: 65536K

 

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. 


题目大意:如果A喜欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你

算出有多少头奶牛可以当被所有的牛所喜爱。

第一行:两个用空格分开的整数:N和M

第二行到第M + 1行:每行两个用空格分开的整数:A和B,表示A喜欢B

。。。上面的样例太水了,自己造了一个比较容易懂的样例:

5 7
1 2
2 1 
4 5
4 1
5 3
3 5
2 3

它的图应该是这样的:

通过tarjan缩点(假装你们懂了QWQ)之后应该是这样的:

也就是说我们只需要找到在缩点后出度为0的点就行了。那么很显然集合为3,5的这个大点就是了。然后我们再从1到n循环一遍一旦遇到编号为这个大点的i我们就将答案+1。当然缩点之后如果有两个及以上出度为0的点,那么也就是说没有答案了。

那么我们怎么找出度为零的点呢?实际上我们把有出度的点记录一下就好了:

for (int i = 1; i <= n; i++) {
	for (int j = head[i]; j != -1; j = eg[j].next) {
		if (id[i] != id[eg[j].to]) vis[id[i]]++;
	}
}

其中id[i]代表第i个点所在的第id[i]个团。于是接下来我们for一遍从1到团的个数比如上面只有3个那么我们只需要for (int i=1; i<=3; i++)就行了。然后对于每一个没有出度的点记录其编号(实际上如果当没有出度的点个数大于1的时候这个编号是废了的)

for (int i = 1; i <= cnt; i++) {
	if (!vis[i]) sum++, x = i;
}

。。。最后统计一下n个点里面有多少个在第x个团里面就好了。

一下是AC代码:
 

//#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int mac = 1e5 + 10;
struct Edge
{
	int to, next;
}eg[mac << 1];
int head[mac], num = 0, dfn[mac], low[mac], stk[mac];
int top = 0, times = 0, cnt = 0, id[mac], vis[mac], instk[mac];
void add(int u, int v);
void tarjan(int x);
int main()
{
	int n, m, x, y;
	memset(head, -1, sizeof(head));
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &x, &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 = head[i]; j != -1; j = eg[j].next) {
			if (id[i] != id[eg[j].to]) vis[id[i]]++;
		}
	}
	int sum = 0;
	for (int i = 1; i <= cnt; i++) {
		if (!vis[i]) sum++, x = i;
	}
	if (sum == 1) {
		int ans = 0;
		for (int i = 1; i <= n; i++)
			if (id[i] == x) ans++;
		printf("%d\n", ans);
	}
	else printf("0\n");
	return 0;
}
void add(int u, int v)
{
	eg[++num].to = v; eg[num].next = head[u];
	head[u] = num;
}
void tarjan(int x)
{
	dfn[x] = low[x] = ++times;
	stk[++top] = x;
	instk[x] = 1;
	for (int i = head[x]; i != -1; i = eg[i].next) {
		int v = eg[i].to;
		if (!dfn[v])
			tarjan(v);
		if (instk[v]) low[x] = min(low[v], low[x]);
	}
	if (low[x] == dfn[x]) {
		cnt++;
		while (1) {
			int v = stk[top--];
			instk[v] = 0;
			id[v] = cnt;
			if (v == x) break;
		}
	}
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值