[USACO03FALL / HAOI2006] 受欢迎的牛 G(C++,强连通分量)

题目背景

本题测试数据已修复。

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 A A A 喜欢 B B B B B B 喜欢 C C C,那么 A A A 也喜欢 C C C。牛栏里共有 N N N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

输入格式

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

接下来 M M M 行:每行两个用空格分开的整数: A A A B B B,表示 A A A 喜欢 B B B

输出格式

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

样例 #1

样例输入 #1

3 3
1 2
2 1
2 3

样例输出 #1

1

提示

只有 3 3 3 号奶牛可以做明星。

【数据范围】

对于 10 % 10\% 10% 的数据, N ≤ 20 N\le20 N20 M ≤ 50 M\le50 M50

对于 30 % 30\% 30% 的数据, N ≤ 1 0 3 N\le10^3 N103 M ≤ 2 × 1 0 4 M\le2\times 10^4 M2×104

对于 70 % 70\% 70% 的数据, N ≤ 5 × 1 0 3 N\le5\times 10^3 N5×103 M ≤ 5 × 1 0 4 M\le5\times 10^4 M5×104

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 4 1\le N\le10^4 1N104 1 ≤ M ≤ 5 × 1 0 4 1\le M\le5\times 10^4 1M5×104

解题思路:

根据题意中的“喜欢可以传递”,在一个爱慕环中的奶牛可以缩成一头奶牛

因为环中任何一头奶牛所喜欢的也被环中其他的奶牛喜欢

喜欢环中任何一头奶牛也会喜欢环中所有的奶牛

采用tarjan缩点,生成一张新图,图中的所有奶牛都是“单相思”

只有图中出度为 0 0 0的节点可能是明星奶牛

因为“单相思”不会得到回应,也就不会符合“被所有奶牛喜欢”这一条件

但是如果有多个出度为 0 0 0的节点,那么就不存在明星奶牛,因为出度为 0 0 0的奶牛不会互相喜欢

AC代码如下

#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
const int max_n = 1e4;
const int max_m = 5e4;

int n, m, u, v;
//链式前向星
int head[max_n + 1];
int tot = -1;
struct edge { int v, next; }edges[max_m];
//tarjan缩点
int timeclock = 0, dfn[max_n + 1], low[max_n + 1];
int in_stack[max_n + 1], stack[max_n], rsp = -1;
//新图
int belong[max_n + 1], power[max_n + 1], cnt = 0;
int out[max_n + 1];//入度

void add_edge(int u, int v) {
	edges[++tot] = { v, head[u] }; head[u] = tot;
}

void tarjan(int s) {
	dfn[s] = low[s] = ++timeclock;
	stack[++rsp] = s;
	in_stack[s] = 1;

	for (int i = head[s]; i != -1; i = edges[i].next) {
		int v = edges[i].v;
		if (!dfn[v]) {
			tarjan(v);
			low[s] = min(low[s], low[v]);
		}
		else if (in_stack[v]) {
			low[s] = min(low[s], low[v]);
		}
	}

	if (dfn[s] == low[s]) {
		cnt++;
		while (stack[rsp + 1] != s) {
			belong[stack[rsp]] = cnt;
			power[cnt]++;//记录合并节点的数量
			in_stack[stack[rsp]] = 0;
			rsp--;
		}
	}
}

int main() {
	memset(head + 1, -1, sizeof(int) * max_n);
	cin >> n >> m;
	for (int i = 0; i < m; i++) {
		cin >> u >> v;
		add_edge(u, v);
	}
	
	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 = edges[j].next) {
			int v = edges[j].v;
			//出度计数
			if (belong[i] != belong[v]) {
				out[belong[i]]++;
			}
		}
	}

	int ans = 0, find = 0;
	for (int i = 1; i <= cnt; i++) {
		if (!out[i]) {
			if (find) {
				cout << 0 << endl;
				return 0;
			}
			else {
				find++;
				ans = i;
			}
		}
	}
	cout << power[ans] << endl;
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WitheredSakura_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值