[HAOI2006]受欢迎的牛

解题报告

原题见COGS
tarjan的典型题、入门题,真的是裸的tarjan。
这道题就是先搜索强连通,再统计强连通的出度,如果有两个或以上出度为零的强连通,则此题无解。
最后输出唯一的强连通的大小,也就是它内部的点的个数。
代码推荐以新页面打开,因为有些注释打的有点长。。//话说这是我第一次写注释把注释打这么详细啊

#include <iostream>
#include <cstdio>
#include <algorithm>
const int MX = 10005;
using namespace std;
int low[MX], dfn[MX], cnt[MX], n, m, be[MX], sccn;
bool out[MX];
class stack {//手写栈
private:
	int s[MX];
	int top_;
	bool instack[MX];
public:
	int top() {
		return s[top_];
	}
	void pop() {
		instack[s[top_]] = false;
		top_--;
	}
	void push(int num) {
		instack[num] = true;
		s[++top_] = num;
	}
	bool in(int num) {
		return instack[num];
	}
}s;
struct edge {
	int to, ne;
}e[MX * 5];//邻接表存边
int head[MX], tot;
void tarjan(int now) {
	int to;
	low[now] = dfn[now] = ++tot;//dfn为当前文件序号, low是一个tarjan独有的特殊数组。。。
	s.push(now);
	for (int i = head[now]; i; i = e[i].ne) {
		to = e[i].to;
		if (!dfn[to]) {//如果之前没有访问过该节点
			tarjan(to);//循环进行深搜
			low[now] = min(low[to], low[now]);//更新low值
		}
		else if (s.in(to)) {//已经访问过该节点,并且是在这次深搜中访问的,表明有环
			low[now] = min(low[to], low[now]);//更新low值
		}
	}
	if (dfn[now] == low[now]) {//进行缩点
			int tmp;
			sccn++;//强连通的编号
			do {
				tmp = s.top();
				s.pop();
				cnt[sccn]++;
				be[tmp] = sccn;//tmp属于的强连通
			}while (tmp != now);
	}
}
void addedge(int x0, int y0) {//邻接表加边
	tot++;
	e[tot].to = y0;
	e[tot].ne = head[x0];
	head[x0] = tot;
}
int get_num() {//日常读入优化
	int ans = 0;
	char tmp;
	tmp = getchar();
	while (tmp > '9' || tmp < '0')	tmp = getchar();
	while (tmp <= '9' && tmp >= '0') {
		ans = ans * 10 + tmp - 48;
		tmp = getchar();
	}
	return ans;
}
int Main() {
	//freopen("cow.in", "r", stdin);
	//freopen("cow.out", "w", stdout);
	n = get_num();
	m = get_num();
	int x, y;
	for (int i = 1; i <= m; i++) {
		x = get_num();
		y = get_num();
		addedge(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; j = e[j].ne) {
			if (be[i] != be[e[j].to]) {//如果该节点和它指向的节点不属于同一个强连通
				out[be[i]] = true;//就说明这个强连通有出度
				break;//然后证明这个节点不是我们要找的答案,可以直接遍历下一个节点
			}
		}
	}
	int tmp = 0;
	int ans = 0;
	for (int i = 1; i <= sccn; i++) {
		if (!out[i]) {//如果这个节点没有出度
			ans = cnt[i];//更新答案
			tmp++;//没有出度的强连通的个数
		}
		if (tmp == 2) {//如果有一个以上的没有出度的强连通,则此题无解
			cout << "0" << endl;
			return 0;
		}
	}
	cout << ans << endl;
	return 0;
}
int AA = Main();//对于COGS的一点神奇的优化,至今不明原理
int main() {;}

 

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理