Description
Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.
Input
* Line 1: Two space-separated integers, N and M
* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.
Output
* Line 1: A single integer that is the number of cows who are considered popular by every other cow.
Sample Input
3 3
1 2
2 1
2 3
Sample Output
1
Hint
Cow 3 is the only cow of high popularity.
题目大意 : 输入一个有向图(可能不连通),输出一个能够让所有点都到达的点集的数目
话不多说先上几张图找找规律 QAQ
这张图最受欢迎的是6号牛, 1、2、3、4、5都不被6号“欢迎”(众星拱月啊)
看不出规律的话再看这张
这下看明白了把!4号牛是最受欢迎的牛,两张图最受欢迎的都是出度为0的点。具体原因可以这么想,把有向图当作一条路,你从起点一直走到终点,一定是走到终点的时候走的步数最多,这时候我们再把特殊情况给排除掉就OK了,这道题的特殊情况就是如果图不连通,一定有无法到达的点,那么直接输出0。
不难看出这是和强连通图有关的,所以我们先用tarjan缩点,并且把每个点集合中元素的个数给记下来,最后只要判断出度是否为0就好。
如果对缩点后判断出度为0有点模糊的话,再看看这张图,应该能够让你理解!
左边是原图,右边的缩点后的图,缩点后1到2、3之间的那条路,就是1集合中所有元素到2、3路的并集,如果原来点U到点V有路,但是缩点后他们不在一个集合了,那么在新图里,他们之间就有入度或者出度的变化了!
AC代码 :
#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int maxn = 1e5 + 5;
struct node
{
int v, next;
}e[maxn];
int dfn[maxn], low[maxn], suo[maxn], cnt, tot;
int head[maxn], out[maxn], sum[maxn], n, m, scnt;
bool vis[maxn];
stack <int> st;
void add (int from, int to) {
e[++cnt].v = to;
e[cnt].next = head[from];
head[from] = cnt;
}
void tarjan (int u) {
dfn[u] = low[u] = ++tot;
vis[u] = 1;
st.push(u);
for (int i = head[u]; i != -1; i = e[i].next) {
if (!dfn[e[i].v]) {
tarjan(e[i].v);
low[u] = min (low[u], low[e[i].v]);
}
else if (vis[e[i].v]) low[u] = min (low[u], dfn[e[i].v]);
}
if (low[u] == dfn[u]) {
scnt++; // 缩点后定点的个数
int k;
do {
k = st.top();
st.pop();
suo[k] = scnt; // 另一个集合中的元素的suo值相等,表示他们在一个集合中
sum[scnt]++; //记录每个集合的元素个数
vis[k] = 0;
}
while (k != u);
}
}
int main()
{
cin >> n >> m;
memset(head, -1, sizeof(head));
for (int i = 0; i < m; i++) {
int ui, vi;
cin >> ui >> vi;
add (ui, vi);
}
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 = e[j].next) {
if (suo[i] != suo[e[j].v]) out[suo[i]]++; //原来有路,新图无路,出度 + 1
}
}
int ans = 0, num = 0;
for (int i = 1; i <= scnt; i++) {
if (!out[i]) num++, ans += sum[i];
}
if (num > 1) cout << 0 << endl; //如果出度为0的超过1了,说明图不连通
else cout << ans << endl;
return 0;
}