You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

80 lines
3.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
const int N = 10010, M = 50010;
int n, m; // n个点m条边
int d[N]; // 记录一下每个强连通分量的出度
// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
// Tarjan算法求强连通分量
int stk[N], top, in_stk[N]; // stk[N]:堆栈,top:配合堆栈使用的游标top,in_stk[N]:是否在栈内
int dfn[N], ts, low[N]; // dfn[N]:时间戳记录数组ts:时间戳游标, low[N]:从u开始走所能遍历到的最小时间戳
int id[N], scc_cnt, sz[N]; // id[N]:强连通分量块的编号,scc_cnt:强连通分量的编号游标,sz[i]:编号为i的强连通分量中原来点的个数
void tarjan(int u) {
dfn[u] = low[u] = ++ts; // 初始时间戳
stk[++top] = u; // 入栈
in_stk[u] = 1; // 在栈内
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (!dfn[v]) { // v未访问过
tarjan(v); // dfs深搜
low[u] = min(low[u], low[v]); // 更新low[u]
} else if (in_stk[v]) // v已经栈中,找到强连通分量
low[u] = min(low[u], dfn[v]); // 更新low[u]
}
// 发现强连通分量
if (dfn[u] == low[u]) {
++scc_cnt;
int x;
do {
x = stk[top--];
in_stk[x] = 0;
id[x] = scc_cnt; // 记录x是缩点后编号为ssc_cnt号强连通分量的一员
sz[scc_cnt]++; // 缩点后编号为ssc_cnt号强连通分量中的成员个数+1
} while (x != u);
}
}
int main() {
scanf("%d %d", &n, &m);
memset(h, -1, sizeof h); // 初始化邻接表
for (int i = 1; i <= m; i++) {
int a, b;
scanf("%d %d", &a, &b);
add(a, b);
}
//(1)求强连通分量,缩点
for (int i = 1; i <= n; i++) // 需示枚举每个点出发尝试,否则会出现有的点没有遍历到的情况
if (!dfn[i]) tarjan(i);
//(2)缩点后其实就是一个DAG计算出DAG中每个新节点的出度
for (int u = 1; u <= n; u++) // 枚举原图中每个出边,用法好怪异~
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i]; // u->v
int a = id[u], b = id[v]; // u,v分别在哪个强连通分量中
if (a != b) d[a]++; // a号强连通分量,也可以理解为是缩点后的点号,出度++
}
//(3) 出度为0的只能有1个如果大于1个就无解如果正好是一个返回此强连通分量中结点的个数
int zeros = 0, res = 0;
for (int i = 1; i <= scc_cnt; i++) // 枚举强连通分量块
if (!d[i]) { // 如果出度是0
zeros++; // 出度是0的强连通分量个数+1
res = sz[i]; // 累加此强连通分量中点的个数
if (zeros > 1) { // 如果强连通分量块的数量大于1个则无解
res = 0;
break;
}
}
//(4)求有多少头牛被除自己之外的所有牛认为是受欢迎的
printf("%d\n", res);
return 0;
}