题意:
有m个有序对,且满足传递性。求解满足以下性质的点的个数,从其他每个点都可以通过有向边到达该点。
思路:
如果A, B都满足性质,则A,B至少在一个有向环上,所以满足性质的点属于同一连通分量。
我们可以用双向dfs算法求出SCC,并且从最后一个连通分量拿一个元素来检查是否可以沿着反图访问到每个点。
//#include<bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cassert>
#include <algorithm>
#include <cmath>
#include <climits>
#include <set>
#include <map>
using namespace std;
#define SPEED_UP iostream::sync_with_stdio(false);
#define FIXED_FLOAT cout.setf(ios::fixed, ios::floatfield);
#define rep(i, s, t) for(int (i)=(s);(i)<=(t);++(i))
#define urep(i, s, t) for(int (i)=(s);(i)>=(t);--(i))
#define in_bound(l, r, i) (l)<=(i)&&(i)<(r)
#define pb push_back
typedef long long LL;
struct Edge{
int from, to;
};
const int Maxn = 10000+100;
const int inf = INT_MAX/2;
int used[Maxn], sccNo[Maxn], n, m;
vector<int> g[Maxn], rg[Maxn], vs;
void dfs(int now) {
used[now] = 1;
int sz = g[now].size(), v;
rep(i, 0, sz-1) {
v = g[now][i];
if (!used[v]) dfs(v);
}
vs.push_back(now);
}
void rdfs(int now, int flag) {
used[now] = 1;
sccNo[now] = flag;
int sz = rg[now].size(), v;
rep(i, 0, sz-1) {
v = rg[now][i];
if (!used[v]) rdfs(v, flag);
}
}
int scc(int V) {
memset(used, 0, sizeof(used));
vs.clear();
//rep(i, 0, V-1)
// if (!used[i]) dfs(i);
urep(i, V-1, 0)
if (!used[i]) dfs(i);
memset(used, 0, sizeof(used));
int cnt = 0;
urep(i, V-1, 0)
if (!used[vs[i]]) rdfs(vs[i], cnt++);
return cnt;
}
void add_edge(int from, int to) {
g[from].push_back(to);
rg[to].push_back(from);
}
void init() {
rep(i, 0, n-1) {
g[i].clear();
rg[i].clear();
}
rep(i, 0, m-1) {
int x, y;
cin >> x >> y;
add_edge(x-1, y-1);
}
}
int solve() {
int tot = scc(n);
int num = 0, u;
rep(i, 0, n-1)
if (sccNo[i] == tot-1) {
++num;
u = i;
}
memset(used, 0, sizeof(used));
rdfs(u, tot-1);
rep(i, 0, n-1)
if (!used[i]) return 0;
return num;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input.in", "r", stdin);
#endif
SPEED_UP
while (cin >> n >> m) {
init();
cout << solve() << endl;
}
return 0;
}