洛谷 P2341 [HAOI2006]受欢迎的牛(强连通分量)

23 篇文章 1 订阅

题目背景

本题测试数据已修复。

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶

牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜

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

算出有多少头奶牛可以当明星。

输入输出格式

输入格式:

 

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

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

 

输出格式:

 

 第一行:单独一个整数,表示明星奶牛的数量

 

输入输出样例

输入样例#1: 

3 3
1 2
2 1
2 3

输出样例#1: 

1

说明

只有 3 号奶牛可以做明星

【数据范围】

10%的数据N<=20, M<=50

30%的数据N<=1000,M<=20000

70%的数据N<=5000,M<=50000

100%的数据N<=10000,M<=50000

解题思路

如果喜欢一个强连通分量里的任意一只牛,那就是喜欢里面的所有牛,如果强连通分量里的任意一只牛喜欢令一只牛,那么强连通分量里的所有牛都喜欢那只牛,所以可以把一个强连通分量视为一个点,把所有强连通分量都视为一个点之后,图中就不再存在环。当存在且只存在一个出度为0的强连通分量时,那个强连通分量中的所有牛就是明星牛。假设一个强连通分量里的牛是明星牛,就说明他们被所有牛喜欢,如果他们也喜欢其他的牛,那么就有环,而此时图中是不存在环的,所以不可能。如果存在多个出度为0的强连通分量,分量中的牛不喜欢其他的牛,那就不会有牛被所有牛喜欢了。

代码如下

#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
#include <stack>
#include <cmath>
#define maxn 10005
using namespace std;
vector<int> g[maxn];
bool vis[maxn];
int dfn[maxn], low[maxn];
int unit[maxn];
stack<int> sta;
int id;
int cnt;
int ans;
bool tarjan(int x)
{
	id ++;
	dfn[x] = low[x] = id;
	sta.push(x);
	vis[x] = true;
	for(int i = 0; i < g[x].size(); i ++){
		int y = g[x][i];
		if(!dfn[y]){
			if(!tarjan(y))
				return false;
			low[x] = min(low[x], low[y]);
		}
		else if(vis[y])
			low[x] = min(low[x], dfn[y]);
	}
	if(dfn[x] == low[x]){
		queue<int> que;
		cnt ++;
		while(!sta.empty()){
			int top = sta.top();
			sta.pop();
			vis[top] = false;
			unit[top] = cnt; //记录这些牛属于哪个强连通分量 
			que.push(top);   //存此分量中的元素 
			if(top == x)
				break;
		}
		bool flg = true;
		int temp = que.size();
		while(!que.empty()){   //判断此强连通分量的出度是否为0 
			int top = que.front();
			que.pop();
			for(int i = 0; i < g[top].size(); i ++){
				int r = g[top][i];
				if(unit[r] != unit[top]){  //判断是不是属于同一个强连通分量 
					flg = false;
					break;
				}
			} 
		}
		if(flg){   //如果这个强连通分量出度为0 
			if(!ans)  //第一个出度为0的 
				ans = temp;  //存个数 
			else {     //如果不止一个 
				ans = 0; //那么没有明星奶牛 
				return false;  //结束递归 
			}
		}
	}
	return true;	
}
int main()
{
	int n, m;
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)
		g[0].push_back(i);
	for(int i = 0; i < m; i ++){
		int a, b;
		cin >> a >> b;
		g[a].push_back(b);
	}
	id = 0;
	cnt = 0;
	ans = 0;
	memset(vis, 0, sizeof(vis));
	memset(dfn, 0, sizeof(dfn));
	memset(unit, 0, sizeof(unit));
	tarjan(0);	
	cout << ans << endl;
	return 0;
} 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值