题目大意
现在有 N N N 头牛,给你 M M M 对整数 ( A , B ) (A,B) (A,B),表示牛 A A A 认为牛 B B B 受欢迎。 这种关系是具有传递性的,如果 A A A 认为 B B B 受欢迎, B B B 认为 C C C 受欢迎,那么牛 A A A 也认为牛 C C C 受欢迎。你的任务是求出有多少头牛被所有的牛认为是受欢迎的。
注意:
给出的信息有可能重复,即有可能出现多个 A A A, B B B。
数据范围:
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 4 1\le N\le10^4 1≤N≤104, 1 ≤ M ≤ 5 × 1 0 4 1\le M\le5\times 10^4 1≤M≤5×104。
解题思路
一眼看到数据范围就可以想到用 Tarjan
进行 缩点
。
关于 Tarjan
,可以看本蒟蒻的博客。
关于 缩点
,也可以看本蒟蒻的博客。
如果一个强连通分量内的一点
x
x
x,可以到除这个强连通分量的点
y
y
y,就说明点
x
x
x 所在的强连通分量的所有点都可以到达点
y
y
y,所以就需要 缩点
。
这时,可以发现答案只能是一个强连通分量,而不是两个或多个。
此时,可以有一个定理:
缩点后的图是一个
DAG
(有向无环图)。
关于本题,又可以有一个定理:
若缩点后的图出度为 0 0 0 的点只有一个,这个点就是答案。
证明:如果出度为 0 0 0 的点有多个呢?
显然,如果出度为 0 0 0 的点有x
个,设为a1、a2、a3、a4 ... ax
,那么假设有一个点为答案,但是 a a a 集合内的所有点都不与它连通,所以这个点就不是答案,那么可以得出, a a a 集合内点使得这组输入没有答案。
证明:为什么缩点后的图出度为 0 0 0 的点只有一个,这个点就是答案。
假设点 x x x 是答案且他的出度不为 0 0 0。即所有点都可以到 x x x,但可以发现一个问题,点 x x x 和点 x x x 所连向的点应该在同一个强连通分量内,即形成了环,因为上面说了缩点后的图是一个DAG
(有向无环图),所以设据得假。
AC CODE
省略快读,orz
。
#include <bits/stdc++.h>
using namespace std;
inline int read()
{
char c = getchar();
int x = 0;
bool f = 0;
for (; !isdigit(c); c = getchar())
{
f ^= !(c ^ 45);
}
for (; isdigit(c); c = getchar())
{
x = (x << 1) + (x << 3) + (c ^ 48);
}
if (f)
{
x = -x;
}
return x;
}
inline void write(int x)
{
if (x < 0)
{
putchar('-');
x = -x;
}
if (x > 9)
{
write(x / 10);
}
putchar(x % 10 + '0');
}
int T, n, m, ans, opt, cnt_node, cntn;
array<int, 200005> p;
int cnt;
array<int, 200005> head;
struct abc
{
int to, nxt;
};
array<abc, 200005> dd;
int cnt_;
array<int, 200005> head_, out; //, ind;
array<abc, 200005> dd_;
array<bool, 200005> vis;
array<int, 200005> dfn, low, id;
stack<int> s;
array<int, 200005> dis;
queue<int> q;
inline void add(int u, int v)
{
dd[++cnt].to = v;
dd[cnt].nxt = head[u];
head[u] = cnt;
}
inline void add_(int u, int v)
{
dd_[++cnt_].to = v;
dd_[cnt_].nxt = head_[u];
head_[u] = cnt_;
}
inline void tarjan(int u)
{
dfn[u] = low[u] = ++cnt_node;
s.push(u);
vis[u] = 1;
for (int e = head[u]; e; e = dd[e].nxt)
{
if (!dfn[dd[e].to])
{
tarjan(dd[e].to);
low[u] = min(low[dd[e].to], low[u]);
}
else if (vis[dd[e].to])
low[u] = min(low[u], dfn[dd[e].to]);
}
if (low[u] == dfn[u])
{
cntn++;
while (1)
{
int now = s.top();
s.pop();
vis[now] = 0;
id[now] = cntn;
p[cntn]++;
if (now == u) break;
}
}
}
void build()
{
for(int i = 1; i <= n; ++i)
for(int j = head[i]; j; j = dd[j].nxt)
{
int v = dd[j].to;
if(id[i] != id[v])
{
// add_(id[i], id[v]);
// cout<<id[i] <<" "<<id[v]<<"\n";
// ind[id[v]]++;
out[id[i]]++;
}
}
}
//void topu()
//{
// for(int i = 1; i <= cntn; ++i)
// if(!ind[i])
// {
// q.push(i);
// dis[i] = p[i];
cout<<i<<endl;
// }
// while(!q.empty())
// {
// int x = q.front();
// q.pop();
// for(int i = head_[x]; i; i = dd_[i].nxt)
// {
// int v = dd_[i].to;
// dis[v] = dis[x] + p[v];
// ind[v]--;
// if(!ind[v]) q.push(v);
// }
// }
//}
signed main()
{
// freopen("input.txt", "r", stdin);freopen("15.out", "w", stdout);
n = read();
m = read();
for(int i = 1; i <= m; ++i)
{
int a, b;
a = read();
b = read();
add(a, b);
}
for(int i = 1; i <= n; ++i)
if(!dfn[i]) tarjan(i);
build();
for(int i = 1; i <= cntn; ++i)
if(!out[i]) opt++, ans = p[i];
if(opt == 1) write(ans);
else write(0);
// topu();
// for(int i = 1; i <= cntn; ++i)
// if(dis[i] == n) ans += p[i];
return 0;
}