受欢迎的牛(强连通分量+压缩点)

受欢迎的牛(popular)

Description

原题来自:USACO 2003 Fall

每一头牛的愿望就是变成一头最受欢迎的牛。现在有 N 头牛,给你 M 对整数 (A,B),表示牛 A 认为牛 B 受欢迎。这种关系是具有传递性的,如果 A 认为 B 受欢迎,B 认为 C 受欢迎,那么牛 A 也认为牛 C 受欢迎。你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。

Input

第一行两个数 N,M;

接下来 M 行,每行两个数 A,B,意思是 A 认为 B 是受欢迎的(给出的信息有可能重复,即有可能出现多个 A,B)。

Output

输出被除自己之外的所有牛认为是受欢迎的牛的数量。

Sample Input 1

3 3
1 2
2 1
2 3

Sample Output 1

1

Hint

样例说明

只有第三头牛被除自己之外的所有牛认为是受欢迎的。

数据范围:

对于全部数据,1≤N≤10,000;1≤M≤50,000

题目大意:

找出被除自己以外所有牛欢迎的牛,典型的强连通分量,找出所有的强连通分量,然后进行压点,如果想满足题目要求,就必须让所有点中出度为0的点有且仅有一个。

#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e4+5;
int low[maxn],instack[maxn],dfn[maxn];
int Stack[maxn],in[maxn],t[maxn],tt[maxn];
int tot,num,cnt;
int n,m;

struct node
{
	int v;
	node(int vv):v(vv){}
};
vector<node>e[maxn];

void add_edge(int u , int v)
{
	e[u].push_back(node(v));
}

void tarjan(int u)
{
	dfn[u] = low[u] = ++tot;
	Stack[++num] = u;
	instack[u] = 1;
	for(int i = 0 ; i < e[u].size() ; i++)
	{
		int v = e[u][i].v;
		if(dfn[v] == 0)
		{
			tarjan(v);
			low[u] = min(low[u],low[v]);
		}
		else if(instack[v])
		{
			low[u] = min(low[u],dfn[v]);
		}
	}
	if(dfn[u] == low[u])
	{
		while(u != Stack[num])
		{
			t[Stack[num]] = cnt;
			tt[cnt]++;
			instack[Stack[num]] = 0;
			num--;
		}
		t[Stack[num]] = cnt;
		tt[cnt]++;
		instack[Stack[num]] = 0;
		num--;
		cnt++;
	}
}


int main()
{
	cin >> n >> m;
	for(int i = 0 ; i < m ; i++)
	{
		int u,v;
		cin >> u >> v;
		add_edge(u,v);	
	}
	for(int i = 1 ; i <= n ; i++)
	{
		if(!dfn[i])
			tarjan(i);
	}
	int sum = 0;
	for(int i = 1 ; i <= n ; i++)
	{
		for(int j = 0 ; j < e[i].size() ; j++)
		{
			int v = e[i][j].v;
			if(t[i] != t[v])
				in[t[i]]++;
		}
	}
	int flag = 0;
	for(int i = 0 ; i < cnt ; i++)
	{
		if(!in[i])
		{
			flag++;
			sum = tt[i];
		}
	}
	if(flag == 1)
		cout << sum << endl;
	else
		cout << "0" << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬ya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值