Problem:
Road Improvement
Aizu is a country famous for its rich tourism resources and has
N
cities, each of which is uniquely identified with a number (0 to
N
-1). It has a road network consisting of
M
one-way roads connecting a city to another.
All the roads connecting the cities in Aizu have a row of cherry trees along their routes. For enhancing the cherry-viewing experience, a proposal was made to modify the road network so that a tourist can travel around all the roads. To achieve this target, it was decided to construct several one-way roads, each connecting two cities and abiding by the following rules.
The newly constructed road is for one-way traffic
Starting from any city, a tourist is able to make a roundtrip and return to the city, whereby he/she drives all the roads exhaustively, including the newly constructed ones. Multiple passages of some of the roads are allowed.
You, as a tourism promotion officer, are assigned with the task of writing a program for the road construction project.
Write a program to determine the minimum number of roads to be constructed given the road network information in Aizu.
Input
The input is given in the following format.
N
M
s_1
t_1
s_2
t_2
:
s_M
t_M
The first line provides the number of cities
N
(1 ≤
N
≤ 10
4
) and roads
M
(0 ≤
M
≤ 10
5
). Each of the subsequent
M
lines provides the numbers assigned to start and destination cities for the
i
-th road:
s_i
,
t_i
(0 ≤
s_i
,
t_i
≤
N
-1) , where
s_i ≠ t_i
. (no duplicate appearance of a road)
Output
Output the minimum number of roads to be newly constructed.
Sample Input 1
6 7
0 2
2 1
1 0
2 3
4 3
4 5
5 4
Sample Output 1
2
Sample Input 2
6 9
0 2
2 1
1 0
2 3
4 3
4 5
5 4
5 2
3 4
Sample Output 2
0

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ld=long double;
#define fi first
#define se second
#define i128 __int128_t
#define all(x) x.begin(),x.end()
#define rep(i,n) for(int i=0;i<(int)(n);++i)
#define si(a) (long)(a.size())
template<typename T>bool chmin(T&a,T b){return b<a?(a=b,1):0;}
template<typename T>bool chmax(T&a,T b){return b>a?(a=b,1):0;}
const int INF=1e9+100;
const long INFL=3e18+100;
struct _ {
   _() { cin.tie(0)->sync_with_stdio(0), cout.tie(0); }
} __;

struct SCC {
    vector<vector<int>> g,rg;
    vector<int> comp, ord, used;
    int num;

    SCC(vector<vector<int>> g) : g(g), rg(si(g)), comp(si(g), -1), ord(si(g)), used(si(g)) {
        rep(i, si(g)) for(auto&& e: g[i]) rg[e].emplace_back(i);
        build();
    };
    int operator[](int k) { return comp[k]; }
    void dfs(int x) {
        if (used[x]) return;
        used[x] = true;
        for(auto&& e : g[x]) if (!used[e]) dfs(e);
        ord.emplace_back(x);
    }
    void rdfs(int x, int cnt) {
        if (comp[x] != -1) return;
        comp[x] = cnt;
        for(auto&& e: rg[x]) if (comp[e] == -1) rdfs(e, cnt);
    }
    void build() {
        rep(i, g.size()) dfs(i);
        reverse(all(ord));
        num = 0;
        for(auto&& i: ord) if (comp[i] == -1) { rdfs(i, num), num++; }
    }
    vector<vector<int>> groups(){
        vector<vector<int>> gp(num);
        for (int v = 0; v < (int)g.size(); v++) {
            gp[comp[v]].push_back(v);
        }
        return gp;
    }
};


int main(){
    int N,M;
    cin>>N>>M;
    vector<vector<int>> G(N);
    rep(i,M){
        int s,t;
        cin>>s>>t;
        G[s].push_back(t);
    }
    SCC scc(G);
    vector<vector<int>> H(scc.num);
    rep(i,N) for(int e: G[i]) {
        if(scc.comp[i]==scc.comp[e]) continue;
        H[scc.comp[i]].push_back(scc.comp[e]);
    }
    rep(i,scc.num) sort(all(H[i])), H[i].erase(unique(all(H[i])),H[i].end());


    if(scc.num==1){
        cout<<0<<"\n";
        return 0;
    }
    
    vector<int> in(scc.num,0),out(scc.num,0);
    rep(i,scc.num) out[i]=H[i].size();
    rep(i,scc.num) for(int e: H[i]) in[e]++;
    int i0=0,o0=0;
    rep(i,scc.num) o0+=(out[i]==0);
    rep(i,scc.num) i0+=(in[i]==0);
    cout<<max(o0,i0)<<"\n";
}
accuracy: 1
time_ms: 10
memory_kb: 7424
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,N) for(int i=0;i<(ll)(N);i++)
#define fi first 
#define se second
#define pb push_back
#define all(X) (X).begin(),(X).end()
ll gcd(ll a,ll b){
    if(b==0)return a;
    return gcd(b,a%b);
}

class segtree{
    public:
    int N;
    vector<ll>data;
    vector<ll>lazy;
    void init (int n){
        N = 1;
        while(N<n)N*=2;
        data.assign(N*2-1,0);
        lazy.assign(N*2-1,0);
    }
    void eval(int k){
        if(lazy[k]==0)return;
        data[k]+=lazy[k];
        if(2*k+2<N*2-1){
            lazy[2*k+1]+=lazy[k];
            lazy[2*k+2]+=lazy[k];
        }
        lazy[k]=0;
    }
    void update(int a,int b,ll x,int k=0,int l=0,int r = -1){
        if(r<0)r=N;
        eval(k);
        if(b<=l||r<=a)return;
        if(a<=l&&r<=b){
            lazy[k]+=x;
            eval(k);
            return;
        }
        update(a,b,x,2*k+1,l,(l+r)/2);
        update(a,b,x,2*k+2,(l+r)/2,r);
        data[k]=max(data[2*k+1],data[2*k+2]);
    }
    ll get(int a,int b,int k=0,int l=0,int r = -1){
        if(r<0)r=N;
        eval(k);
        if(b<=l||r<=a)return 0;
        if(a<=l&&r<=b){
            return data[k];
        }
        ll L=get(a,b,k*2+1,l,(l+r)/2),R=get(a,b,k*2+2,(l+r)/2,r);
        return max(L,R);
    }
};

template< typename G >
struct StronglyConnectedComponents {
  const G &g;
  vector<vector<int>> gg, rg;
  vector< int > comp, order, used;

  StronglyConnectedComponents(G &g) : g(g), gg(g.size()), rg(g.size()), comp(g.size(), -1), used(g.size()) {
    for(int i = 0; i < g.size(); i++) {
      for(auto e : g[i]) {
        gg[i].emplace_back((int) e);
        rg[(int) e].emplace_back(i);
      }
    }
  }

  int operator[](int k) {
    return comp[k];
  }

  void dfs(int idx) {
    if(used[idx]) return;
    used[idx] = true;
    for(int to : gg[idx]) dfs(to);
    order.push_back(idx);
  }

  void rdfs(int idx, int cnt) {
    if(comp[idx] != -1) return;
    comp[idx] = cnt;
    for(int to : rg[idx]) rdfs(to, cnt);
  }

  void build(vector<vector<int>> &t) {
    for(int i = 0; i < gg.size(); i++) dfs(i);
    reverse(begin(order), end(order));
    int ptr = 0;
    for(int i : order) if(comp[i] == -1) rdfs(i, ptr), ptr++;

    t.resize(ptr);
    for(int i = 0; i < g.size(); i++) {
      for(auto &to : g[i]) {
        int x = comp[i], y = comp[to];
        if(x == y) continue;
        t[x].push_back(y);
      }
    }
  }
};

int main(){
    int N,M;cin>>N>>M;
    vector<vector<int>>G(N);
    rep(i,M){
        int s,t;cin>>s>>t;
        G[s].pb(t);
    }
    StronglyConnectedComponents SCC(G);
    vector<vector<int>>nG;
    SCC.build(nG);
    if(nG.size()==1){
        cout<<0<<endl;
        return 0;
    }
    vector<int>inc(nG.size(),0);
    ll ct = 0;
    rep(i,nG.size()){
        if(nG[i].size()==0)ct++;
        for(auto e : nG[i]){
            inc[e]++;
        }
    }
    ll z = 0;
    for(auto e : inc){
        if(e==0)z++;
    }
    cout<<max(z,ct)<<endl;
}
accuracy: 1
time_ms: 20
memory_kb: 7296
final_rank: 4

code_snippet: #include <bits/stdc++.h>
#include <iomanip>
#include <numbers>
#include <cassert>
#include <numeric>
#ifdef _MSC_VER
#include <intrin.h>
#endif
using namespace std;
using ll = long long;
using ull = unsigned long long;
using pint = pair<int, int>;
using pll = pair<ll, ll>;
template<typename T>
using min_priority_queue = priority_queue<T, vector<T>, greater<T>>;
#define rep(i, n) for(int i = 0; i < (n); ++i)
template<typename T>
bool chmax(T & a, const T & b) { if (a < b) { a = b; return true; } return false; }
template<typename T>
bool chmin(T & a, const T & b) { if (a > b) { a = b; return true; } return false; }
const int inf = 1e9;
const ll linf = 1e18;
const int dy[] = { 0,0,1,-1 }, dx[] = { 1,-1,0,0 };
namespace atcoder {
    namespace internal {

        template <class E> struct csr {
            std::vector<int> start;
            std::vector<E> elist;
            csr(int n, const std::vector<std::pair<int, E>>& edges)
                : start(n + 1), elist(edges.size()) {
                for (auto e : edges) {
                    start[e.first + 1]++;
                }
                for (int i = 1; i <= n; i++) {
                    start[i] += start[i - 1];
                }
                auto counter = start;
                for (auto e : edges) {
                    elist[counter[e.first]++] = e.second;
                }
            }
        };

        struct scc_graph {
        public:
            scc_graph(int n) : _n(n) {}

            int num_vertices() { return _n; }

            void add_edge(int from, int to) { edges.push_back({ from, {to} }); }

            std::pair<int, std::vector<int>> scc_ids() {
                auto g = csr<edge>(_n, edges);
                int now_ord = 0, group_num = 0;
                std::vector<int> visited, low(_n), ord(_n, -1), ids(_n);
                visited.reserve(_n);
                auto dfs = [&](auto self, int v) -> void {
                    low[v] = ord[v] = now_ord++;
                    visited.push_back(v);
                    for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                        auto to = g.elist[i].to;
                        if (ord[to] == -1) {
                            self(self, to);
                            low[v] = std::min(low[v], low[to]);
                        }
                        else {
                            low[v] = std::min(low[v], ord[to]);
                        }
                    }
                    if (low[v] == ord[v]) {
                        while (true) {
                            int u = visited.back();
                            visited.pop_back();
                            ord[u] = _n;
                            ids[u] = group_num;
                            if (u == v) break;
                        }
                        group_num++;
                    }
                    };
                for (int i = 0; i < _n; i++) {
                    if (ord[i] == -1) dfs(dfs, i);
                }
                for (auto& x : ids) {
                    x = group_num - 1 - x;
                }
                return { group_num, ids };
            }

            std::vector<std::vector<int>> scc() {
                auto ids = scc_ids();
                int group_num = ids.first;
                std::vector<int> counts(group_num);
                for (auto x : ids.second) counts[x]++;
                std::vector<std::vector<int>> groups(ids.first);
                for (int i = 0; i < group_num; i++) {
                    groups[i].reserve(counts[i]);
                }
                for (int i = 0; i < _n; i++) {
                    groups[ids.second[i]].push_back(i);
                }
                return groups;
            }

        private:
            int _n;
            struct edge {
                int to;
            };
            std::vector<std::pair<int, edge>> edges;
        };

    }

}
namespace atcoder {

    struct scc_graph {
    public:
        scc_graph() : internal(0) {}
        scc_graph(int n) : internal(n) {}

        void add_edge(int from, int to) {
            int n = internal.num_vertices();
            assert(0 <= from && from < n);
            assert(0 <= to && to < n);
            internal.add_edge(from, to);
        }

        std::vector<std::vector<int>> scc() { return internal.scc(); }

    private:
        internal::scc_graph internal;
    };

}
using namespace atcoder;
int main() {
    int n, m; cin >> n >> m;
    scc_graph graph(n);
    vector<vector<int>> g(n);
    rep(i, m) {
        int s, t; cin >> s >> t;
        graph.add_edge(s, t);
        g[s].push_back(t);
    }
    auto scc = graph.scc();
    if (scc.size() == 1) {
        cout << 0 << endl;
        return 0;
    }
    vector<int> idx(n);
    rep(i, scc.size())rep(j, scc[i].size()) {
        idx[scc[i][j]] = i;
    }
    vector<int> icnt(scc.size()), ocnt(scc.size());
    rep(i, n)rep(j, g[i].size()) {
        if (idx[i] != idx[g[i][j]]) {
            icnt[idx[g[i][j]]]++;
            ocnt[idx[i]]++;
        }
    }
    int in = 0, out = 0;
    rep(i, scc.size()) {
        if (icnt[i] == 0)in++;
        if (ocnt[i] == 0)out++;
    }
    cout << max(in, out) << endl;
}
accuracy: 1
time_ms: 20
memory_kb: 7056
final_rank: 3

code_snippet: #include <bits/stdc++.h>

using namespace std;
using LL = long long int;

const int MAX_V = 10005;

int V, M;
vector<int> G[MAX_V];
vector<int> rG[MAX_V];
vector<int> vs;
bool used[MAX_V];
int cmp[MAX_V];
vector<vector<int>> GG;
vector<vector<int>> GGEdge;
vector<int> GInput;

void add_edge(int from, int to)
{
  G[from].push_back(to);
  rG[to].push_back(from);
}

void dfs(int v)
{
  used[v] = true;
  for(unsigned int i = 0; i < G[v].size(); i++) {
    if(!used[G[v][i]]) dfs(G[v][i]);
  }
  vs.push_back(v);
}

void rdfs(int v, int k)
{
  used[v] = true;
  cmp[v] = k; GG[k].push_back(v);
  for(unsigned int i = 0; i < rG[v].size(); i++) {
    if(!used[rG[v][i]]) rdfs(rG[v][i], k);
  }
}

int scc()
{
  memset(used, 0, sizeof(used));
  vs.clear();
  for(int v = 0; v < V; v++) {
    if(!used[v]) dfs(v);
  }
  memset(used, 0, sizeof(used));
  int k = 0;
  for(int i = vs.size()-1; i >= 0; i--) {
    if(!used[vs[i]]) {
      GG.push_back(vector<int>());
      rdfs(vs[i], k++);
    }
  }
  return k;
}

class UnionFind{
public:
  int par[MAX_V];
  UnionFind(){
    init();
  }
  void init() {
    for(int i = 0; i < MAX_V; i++) {
      par[i] = i;
    }
  }
  int find(int x) {
    if(x == par[x]) {
      return x;
    } else {
      return par[x] = find(par[x]);
    }
  }
  void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if(x == y) {
      return;
    }
    par[x] = y;
  }
  bool same(int x, int y) {
    return find(x) == find(y);
  }
}uf;

int solve(int v, bool flag = true)
{
  used[v] = true;
  if(GGEdge[v].size() == 0 && flag) {
    return 0;
  } else if(GGEdge[v].size() == 0) {
    return 1;
  } else {
    int ret = 0;
    for(int next : GGEdge[v]) {
      if(!used[next]) {
        uf.unite(v, next);
        ret += solve(next, false);
      } else {
        if(!uf.same(v, next)){
          ret++;
          uf.unite(v, next);
        }
      }
    }
    return ret;
  }
}

vector<int> GGIn, GGOut;

int main()
{
  cin >> V >> M;
  for(int i = 0; i < M; i++) {
    int f, t; cin >> f >> t;
    add_edge(f, t);
  }

  scc();

  GGEdge.resize(GG.size());
  GGIn.resize(GG.size()); GGOut.resize(GG.size());
  for(unsigned int k = 0; k < GG.size(); k++) {
    //cerr << k << ": ";
    set<int> S; S.insert(k);
    for(unsigned int i = 0; i < GG[k].size(); i++) {
      int v = GG[k][i];
      //cerr << v << " ";
      for(unsigned int j = 0; j < G[v].size(); j++) {
        int next = G[v][j];
        if(S.insert(cmp[next]).second){
          GGEdge[cmp[v]].push_back(cmp[next]);
          GGIn[cmp[next]]++;
          GGOut[cmp[v]]++;
        }

      }
    }
    //cerr << endl;
  }
/*
  for(unsigned int v = 0; v < GG.size(); v++) {
    cerr << v << ": " ;
    for(int next : GGEdge[v]) {
      cerr << next << " ";
    }
    cerr << endl;
  }
*/
/*
  int ans = 0;
  memset(used, 0, sizeof(used));
  for(unsigned int v = 0; v < GG.size(); v++) {
    if(!used[v]) {
      ans += solve(v);
    }
    //cerr << ans << endl;
  }

  set<int> S;
  for(unsigned int v = 0; v < GG.size(); v++) {
    S.insert(uf.find(v));
  }

  //cerr << ans << " " << S.size() << endl;

  if(S.size() > 1) {
    ans += S.size();
  }

  cout << ans << endl;
*/

  if(GG.size()==1) {
    cout << 0 << endl;
    return 0;
  }

  int in = 0, out = 0;
  for(unsigned int i = 0; i < GG.size(); i++) {
    if(GGIn[i] == 0) {
      in++;
    }
    if(GGOut[i] == 0) {
      out++;
    }
  }

  cout << max(in, out) << endl;

  return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 6636
final_rank: 5

code_snippet: #include <bits/stdc++.h>
#define reb(i,n) for (int i = 0; i < (n); i++)
#define rep(i, n0, n) for (int i = (n0); i < (n); i++)
#define repp(i, n0, n) for (int i = (n0); i <= (n); i++)
#define rrep(i, n0, n) for (int i = (n0); i >= (n); i--) 
#define bit(n,k) ((n>>k)&1)
using namespace std;
using ll = long long;
using ull = unsigned long long;
using pl = pair<ll,ll>;
using pi = pair<int,int>;
template<class T> inline bool chmax(T& a, T b) { if(a<b) { a = b; return true;} return false; }
template<class T> inline bool chmin(T& a, T b) { if(a>b) { a = b; return true;} return false; }

void solve() {
    int N, M; cin >> N >> M;
    vector<vector<int>> g(N), r(N);
    reb(i,M) {
        int s, t; cin >> s >> t;
        g[s].emplace_back(t);
        r[t].emplace_back(s);
    }

    int now = 0;
    vector<bool> visited(N);
    vector<int> t(N);
    auto dfs = [&](auto dfs, int v) -> void {
        visited[v] = true;
        for (auto nv : g[v]) {
            if (visited[nv]) continue;
            dfs(dfs,nv);
        }
        t[now++] = v;
    };
    reb(v,N) {
        if (visited[v]) continue;
        dfs(dfs,v);
    }

    int cn = 0;
    vector<unordered_set<int>> groups;
    vector<bool> visited2(N);
    auto dfs2 = [&](auto dfs2, int v) -> void {
        visited2[v] = true;
        groups[cn].emplace(v);
        for (auto nv : r[v]) {
            if (visited2[nv]) continue;
            dfs2(dfs2,nv);
        }
    };
    rrep(d,N-1,0) {
        int v = t[d];
        if (visited2[v]) continue;
        groups.emplace_back(unordered_set<int>());
        dfs2(dfs2,v);
        cn++;
    }

    vector<int> cc(N), indeg(cn), outdeg(cn);
    vector<unordered_set<int>> G(cn), R(cn);
    reb(c,cn) {
        auto grp = groups[c];
        for (int v : grp) {
            cc[v] = c;
        }
    }
    reb(c,cn) {
        auto grp = groups[c];
        for (int v : grp) {
            for (int nv : g[v]) if (!grp.contains(nv) and !G[c].contains(cc[nv])) G[c].emplace(cc[nv]), outdeg[c]++;
            for (int pv : r[v]) if (!grp.contains(pv) and !R[c].contains(cc[pv])) R[c].emplace(cc[pv]), indeg[c]++;
        }
    }

    int ans = max(ranges::count(indeg,0), ranges::count(outdeg,0));
    cout << ((int)groups.size() == 1 ? 0 : ans) << endl;
}

int main() {
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
    solve();

    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 17480
final_rank: 14

code_snippet: #include <bits/stdc++.h>
#define reb(i,n) for (int i = 0; i < (n); i++)
#define rep(i, n0, n) for (int i = (n0); i < (n); i++)
#define repp(i, n0, n) for (int i = (n0); i <= (n); i++)
#define rrep(i, n0, n) for (int i = (n0); i >= (n); i--) 
#define bit(n,k) ((n>>k)&1)
using namespace std;
using ll = long long;
using ull = unsigned long long;
using pl = pair<ll,ll>;
using pi = pair<int,int>;
template<class T> inline bool chmax(T& a, T b) { if(a<b) { a = b; return true;} return false; }
template<class T> inline bool chmin(T& a, T b) { if(a>b) { a = b; return true;} return false; }

void solve() {
    int N, M; cin >> N >> M;
    vector<vector<int>> g(N), r(N);
    reb(i,M) {
        int s, t; cin >> s >> t;
        g[s].emplace_back(t);
        r[t].emplace_back(s);
    }

    int now = 0;
    vector<bool> visited(N);
    vector<int> t(N);
    auto dfs = [&](auto dfs, int v) -> void {
        visited[v] = true;
        for (auto nv : g[v]) {
            if (visited[nv]) continue;
            dfs(dfs,nv);
        }
        t[now++] = v;
    };
    reb(v,N) {
        if (visited[v]) continue;
        dfs(dfs,v);
    }

    int cn = 0;
    vector<unordered_set<int>> groups;
    vector<bool> visited2(N);
    auto dfs2 = [&](auto dfs2, int v) -> void {
        visited2[v] = true;
        groups[cn].emplace(v);
        for (auto nv : r[v]) {
            if (visited2[nv]) continue;
            dfs2(dfs2,nv);
        }
    };
    rrep(d,N-1,0) {
        int v = t[d];
        if (visited2[v]) continue;
        groups.emplace_back(unordered_set<int>());
        dfs2(dfs2,v);
        cn++;
    }

    vector<int> cc(N), indeg(cn), outdeg(cn);
    vector<unordered_set<int>> G(cn), R(cn);
    reb(c,cn) {
        auto grp = groups[c];
        for (int v : grp) {
            cc[v] = c;
        }
    }
    reb(c,cn) {
        auto grp = groups[c];
        for (int v : grp) {
            for (int nv : g[v]) if (!grp.contains(nv) and !G[c].contains(cc[nv])) G[c].emplace(cc[nv]), outdeg[c]++;
            for (int pv : r[v]) if (!grp.contains(pv) and !R[c].contains(cc[pv])) R[c].emplace(cc[pv]), indeg[c]++;
        }
    }

    // vector<bool> visited3(cn);
    // auto dfs3 = [&](auto dfs3, int c) -> void {
    //     visited3[c] = true;
    //     for (int nc : G[c]) {
    //         if (visited3[nc]) continue;
    //         dfs3(dfs3, nc);
    //     }
    //     for (int nc : R[c]) {
    //         if (visited3[nc]) continue;
    //         dfs3(dfs3, nc);
    //     }
    // };

    // int ccnt = 0;
    // reb(c,cn) {
    //     if (visited3[c]) continue;
    //     dfs3(dfs3, c);
    //     ccnt++;
    // }

    int ans = max(ranges::count(indeg,0), ranges::count(outdeg,0));
    cout << ((int)groups.size() == 1 ? 0 : ans) << endl;
}

int main() {
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
    solve();

    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 17484
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

struct StrongConnectedComponents {
    int N;
    vector<int> order, comp;
    vector<vector<int>> G_rev, groups, dag;
    StrongConnectedComponents(const vector<vector<int>>& G) : N(G.size()) {
        // 辺が逆向きのグラフ G_rev を構築
        G_rev.resize(N);
        for (int u = 0; u < N; u++) {
            for (auto&& v : G[u]) {
                G_rev[v].emplace_back(u);
            }
        }
        comp.resize(N, -2);
        // comp[i] = -2: 未訪問, -1: dfs で訪問済, id: dfs2 で訪問済
        for (int i = 0; i < N; i++) {
            if (comp[i] != -2) continue;
            dfs(G, i);
        }
        int id = 0;
        for (int i = N - 1; i >= 0; i--) {
            if (comp[order[i]] != -1) continue;
            dfs2(order[i], id), id++;
        }
        G_rev.clear(), order.clear();
        groups.resize(id), dag.resize(id);
        for (int i = 0; i < N; i++) {
            groups[comp[i]].emplace_back(i);
            for (auto&& to : G[i]) {
                if (comp[i] == comp[to]) continue;
                dag[comp[i]].emplace_back(comp[to]);
            }
        }
    }
    void dfs(const vector<vector<int>>& G, int pos) {
        comp[pos] = -1;
        for (auto&& to : G[pos]) {
            if (comp[to] != -2) continue;
            dfs(G, to);
        }
        order.emplace_back(pos);
    }
    void dfs2(int pos, int id) {
        comp[pos] = id;
        for (auto&& to : G_rev[pos]) {
            if (comp[to] != -1) continue;
            dfs2(to, id);
        }
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N, M;
    cin >> N >> M;
    vector<vector<int>> G(N);
    for (int i = 0; i < M; i++) {
        int u, v;
        cin >> u >> v;
        G[u].emplace_back(v);
    }

    StrongConnectedComponents scc(G);
    int N2 = ssize(scc.groups);

    // 入次数が 0 または出次数が 0 のものを数える
    vector<int> in(N2, 0), out(N2, 0);
    for (int from = 0; from < N2; from++) {
        for (auto&& to : scc.dag[from]) {
            in[to]++, out[from]++;
        }
    }

    int ans = max(ranges::count(in, 0), ranges::count(out, 0));
    cout << (N2 == 1 ? 0 : ans) << '\n';
}
accuracy: 1
time_ms: 10
memory_kb: 6272
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

struct StrongConnectedComponents {
    int N;
    vector<int> order, comp;
    vector<vector<int>> G_rev, groups, dag;
    StrongConnectedComponents(const vector<vector<int>>& G) : N(G.size()) {
        // 辺が逆向きのグラフ G_rev を構築
        G_rev.resize(N);
        for (int u = 0; u < N; u++) {
            for (auto&& v : G[u]) {
                G_rev[v].emplace_back(u);
            }
        }
        comp.resize(N, -2);
        // comp[i] = -2: 未訪問, -1: dfs で訪問済, id: dfs2 で訪問済
        for (int i = 0; i < N; i++) {
            if (comp[i] != -2) continue;
            dfs(G, i);
        }
        int id = 0;
        for (int i = N - 1; i >= 0; i--) {
            if (comp[order[i]] != -1) continue;
            dfs2(order[i], id), id++;
        }
        G_rev.clear(), order.clear();
        groups.resize(id), dag.resize(id);
        for (int i = 0; i < N; i++) {
            groups[comp[i]].emplace_back(i);
            for (auto&& to : G[i]) {
                if (comp[i] == comp[to]) continue;
                dag[comp[i]].emplace_back(comp[to]);
            }
        }
    }
    void dfs(const vector<vector<int>>& G, int pos) {
        comp[pos] = -1;
        for (auto&& to : G[pos]) {
            if (comp[to] != -2) continue;
            dfs(G, to);
        }
        order.emplace_back(pos);
    }
    void dfs2(int pos, int id) {
        comp[pos] = id;
        for (auto&& to : G_rev[pos]) {
            if (comp[to] != -1) continue;
            dfs2(to, id);
        }
    }
};

struct UnionFind {
    int n, counter;
    vector<int> data;
    UnionFind(int size) : n(size), counter(size), data(size, -1) {}
    int root(int x) { return (data[x] < 0 ? x : data[x] = root(data[x])); }
    bool same(int x, int y) { return root(x) == root(y); }
    bool merge(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (-data[x] < -data[y]) swap(x, y);
        data[x] += data[y];
        data[y] = x;
        counter--;
        return true;
    }
    int size(int x) { return -data[root(x)]; }
    int size() { return counter; }
    vector<int> group(int x) {
        vector<int> res;
        for (int i = 0; i < n; i++)
            if (same(x, i)) res.emplace_back(i);
        return res;
    }
    vector<vector<int>> groups() {
        vector<vector<int>> res(n);
        for (int i = 0; i < n; i++) res[root(i)].emplace_back(i);
        res.erase(remove_if(res.begin(), res.end(), [&](const vector<int>& v) { return v.empty(); }), res.end());
        return res;
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N, M;
    cin >> N >> M;
    vector<vector<int>> G(N);
    for (int i = 0; i < M; i++) {
        int u, v;
        cin >> u >> v;
        G[u].emplace_back(v);
    }

    StrongConnectedComponents scc(G);
    int N2 = ssize(scc.groups);

    // dag の連結成分数, 入次数が 0 または出次数が 0 のものを数える
    UnionFind uf(N2);
    vector<int> in(N2, 0), out(N2, 0);
    for (int from = 0; from < N2; from++) {
        for (auto&& to : scc.dag[from]) {
            in[to]++, out[from]++;
            uf.merge(from, to);
        }
    }

    int ans = ranges::count(in, 0);
    cout << (N2 == 1 ? 0 : ans) << '\n';
}
accuracy: 0.5666666666666667
time_ms: 10
memory_kb: 6272
final_rank: 18

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

struct StrongConnectedComponents {
    int N;
    vector<int> order, comp;
    vector<vector<int>> G_rev, groups, dag;
    StrongConnectedComponents(const vector<vector<int>>& G) : N(G.size()) {
        // 辺が逆向きのグラフ G_rev を構築
        G_rev.resize(N);
        for (int u = 0; u < N; u++) {
            for (auto&& v : G[u]) {
                G_rev[v].emplace_back(u);
            }
        }
        comp.resize(N, -2);
        // comp[i] = -2: 未訪問, -1: dfs で訪問済, id: dfs2 で訪問済
        for (int i = 0; i < N; i++) {
            if (comp[i] != -2) continue;
            dfs(G, i);
        }
        int id = 0;
        for (int i = N - 1; i >= 0; i--) {
            if (comp[order[i]] != -1) continue;
            dfs2(order[i], id), id++;
        }
        G_rev.clear(), order.clear();
        groups.resize(id), dag.resize(id);
        for (int i = 0; i < N; i++) {
            groups[comp[i]].emplace_back(i);
            for (auto&& to : G[i]) {
                if (comp[i] == comp[to]) continue;
                dag[comp[i]].emplace_back(comp[to]);
            }
        }
    }
    void dfs(const vector<vector<int>>& G, int pos) {
        comp[pos] = -1;
        for (auto&& to : G[pos]) {
            if (comp[to] != -2) continue;
            dfs(G, to);
        }
        order.emplace_back(pos);
    }
    void dfs2(int pos, int id) {
        comp[pos] = id;
        for (auto&& to : G_rev[pos]) {
            if (comp[to] != -1) continue;
            dfs2(to, id);
        }
    }
};

struct UnionFind {
    int n, counter;
    vector<int> data;
    UnionFind(int size) : n(size), counter(size), data(size, -1) {}
    int root(int x) { return (data[x] < 0 ? x : data[x] = root(data[x])); }
    bool same(int x, int y) { return root(x) == root(y); }
    bool merge(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (-data[x] < -data[y]) swap(x, y);
        data[x] += data[y];
        data[y] = x;
        counter--;
        return true;
    }
    int size(int x) { return -data[root(x)]; }
    int size() { return counter; }
    vector<int> group(int x) {
        vector<int> res;
        for (int i = 0; i < n; i++)
            if (same(x, i)) res.emplace_back(i);
        return res;
    }
    vector<vector<int>> groups() {
        vector<vector<int>> res(n);
        for (int i = 0; i < n; i++) res[root(i)].emplace_back(i);
        res.erase(remove_if(res.begin(), res.end(), [&](const vector<int>& v) { return v.empty(); }), res.end());
        return res;
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N, M;
    cin >> N >> M;
    vector<vector<int>> G(N);
    for (int i = 0; i < M; i++) {
        int u, v;
        cin >> u >> v;
        G[u].emplace_back(v);
    }

    StrongConnectedComponents scc(G);
    int N2 = ssize(scc.groups);

    // dag の連結成分数, 入次数が 0 または出次数が 0 のものを数える
    UnionFind uf(N2);
    vector<int> in(N2, 0), out(N2, 0);
    for (int from = 0; from < N2; from++) {
        for (auto&& to : scc.dag[from]) {
            in[to]++, out[from]++;
            uf.merge(from, to);
        }
    }

    int ans = count(in.begin(), in.end(), 0) + count(out.begin(), out.end(), 0) - uf.size();
    cout << (N2 == 1 ? 0 : ans) << '\n';
}
accuracy: 0.3
time_ms: 10
memory_kb: 5760
final_rank: 20

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

struct StrongConnectedComponents {
    int N;
    vector<int> order, comp;
    vector<vector<int>> G_rev, groups, dag;
    StrongConnectedComponents(const vector<vector<int>>& G) : N(G.size()) {
        // 辺が逆向きのグラフ G_rev を構築
        G_rev.resize(N);
        for (int u = 0; u < N; u++) {
            for (auto&& v : G[u]) {
                G_rev[v].emplace_back(u);
            }
        }
        comp.resize(N, -2);
        // comp[i] = -2: 未訪問, -1: dfs で訪問済, id: dfs2 で訪問済
        for (int i = 0; i < N; i++) {
            if (comp[i] != -2) continue;
            dfs(G, i);
        }
        int id = 0;
        for (int i = N - 1; i >= 0; i--) {
            if (comp[order[i]] != -1) continue;
            dfs2(order[i], id), id++;
        }
        G_rev.clear(), order.clear();
        groups.resize(id), dag.resize(id);
        for (int i = 0; i < N; i++) {
            groups[comp[i]].emplace_back(i);
            for (auto&& to : G[i]) {
                if (comp[i] == comp[to]) continue;
                dag[comp[i]].emplace_back(comp[to]);
            }
        }
    }
    void dfs(const vector<vector<int>>& G, int pos) {
        comp[pos] = -1;
        for (auto&& to : G[pos]) {
            if (comp[to] != -2) continue;
            dfs(G, to);
        }
        order.emplace_back(pos);
    }
    void dfs2(int pos, int id) {
        comp[pos] = id;
        for (auto&& to : G_rev[pos]) {
            if (comp[to] != -1) continue;
            dfs2(to, id);
        }
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N, M;
    cin >> N >> M;
    vector<vector<int>> G(N);
    for (int i = 0; i < M; i++) {
        int u, v;
        cin >> u >> v;
        G[u].emplace_back(v);
    }

    StrongConnectedComponents scc(G);
    int N2 = ssize(scc.groups);

    // dag の連結成分数が答え (dag の頂点数が 1 の場合は 0)
    int ans = 0;
    vector<int> seen(N2);
    for (int i = 0; i < N2; i++) {
        if (!seen[i]) seen[i] = true, ans++;
        for (auto&& to : scc.dag[i]) {
            if (!seen[to]) seen[to] = true;
        }
    }

    cout << (N2 == 1 ? 0 : ans) << '\n';
}
accuracy: 0.5666666666666667
time_ms: 10
memory_kb: 6144
final_rank: 17

code_snippet: #include<bits/stdc++.h>
#define int long long
#define mod 1000000007
#define endl '\n'
#define pii pair<int,int>
#define I_am_weak ios::sync_with_stdio(0),cin.tie(0);
using namespace std;
#define MXN 10055
struct Scc{
    #define PB push_back
  int n, nScc, vst[MXN], bln[MXN];
  vector<int> E[MXN], rE[MXN], vec;
  void init(int _n){
    n = _n;
    for (int i=0; i<MXN; i++)
      E[i].clear(), rE[i].clear();
  }
  void addEdge(int u, int v){
    E[u].PB(v); rE[v].PB(u);
  }
  void DFS(int u){
    vst[u]=1;
    for (auto v : E[u]) if (!vst[v]) DFS(v);
    vec.PB(u);
  }
  void rDFS(int u){
    vst[u] = 1; bln[u] = nScc;
    for (auto v : rE[u]) if (!vst[v]) rDFS(v);
  }
  void solve(){
    nScc = 0;
    vec.clear();
    memset(vst,0,sizeof(vst));
    for (int i=0; i<n; i++)
      if (!vst[i]) DFS(i);
    reverse(vec.begin(),vec.end());
    memset(vst,0,sizeof(vst));
    for (auto v : vec)
      if (!vst[v]){
        rDFS(v); nScc++;
      }
  }
}ggg;

vector<pii>edge;
vector<int>v[10005];
int in[10005];
signed main(){
    //注意多筆測資輸入！！！
    I_am_weak
    int n,m;
    memset(in,0,sizeof(in));
    cin>>n>>m;
    ggg.init(n);
    for(int i=0;i<m;i++){
        int x,y;
        cin>>x>>y;
        edge.push_back({x,y});
        ggg.addEdge(x,y);
    }
    ggg.solve();
    for(auto [x,y]:edge){
        if(ggg.bln[x]!=ggg.bln[y]){
            //cout<<ggg.bln[x]<<" "<<ggg.bln[y]<<endl;
            v[ggg.bln[x]].push_back(ggg.bln[y]);
            in[ggg.bln[y]]++;
        }
    }
    if(ggg.nScc==1){
        cout<<0<<endl;
        return 0;
    }
    int cnt=0;
    for(int i=0;i<ggg.nScc;i++){
        if(in[i]==0)cnt++;
    }
    cout<<cnt<<endl;
    
    
}
accuracy: 0.5666666666666667
time_ms: 10
memory_kb: 9772
final_rank: 19

code_snippet: ﻿// ### test.cpp ###
#include <bits/stdc++.h>
#ifdef __DEBUG_VECTOR
namespace for_debugging{
    struct subscript_and_location{
        int sub;
        std::source_location loc;
        subscript_and_location(int sub_,std::source_location loc_=std::source_location::current()){
            sub=sub_;
            loc=loc_;
        }
        void check_out_of_range(size_t sz){
            if(sub<0||(int)sz<=sub){
                std::clog << loc.file_name() << ":(" << loc.line() << ":" << loc.column() << "):" << loc.function_name() << std::endl;
                std::clog << "out of range: subscript = " << sub << ", vector_size = " << sz << std::endl;
                exit(EXIT_FAILURE);
            }
        }
    };
}
namespace std{
    template<class T,class Allocator=std::allocator<T>> class vector_for_debugging:public std::vector<T,Allocator>{
        using std::vector<T,Allocator>::vector;
        public:
            [[nodiscard]] constexpr std::vector<T,Allocator>::reference operator[](for_debugging::subscript_and_location n) noexcept(!std::is_same<T,bool>::value){
                n.check_out_of_range(this->size());
                return std::vector<T,Allocator>::operator[](n.sub);
            }
            [[nodiscard]] constexpr std::vector<T,Allocator>::const_reference operator[](for_debugging::subscript_and_location n) const noexcept(!std::is_same<T,bool>::value){
                n.check_out_of_range(this->size());
                return std::vector<T,Allocator>::operator[](n.sub);
            }
    };
    namespace pmr{
        template<class T> using vector_for_debugging=std::vector_for_debugging<T,std::pmr::polymorphic_allocator<T>>;
    }
}
#define vector vector_for_debugging
#endif
using namespace std;
using ll = long long;
using ull = unsigned long long;
using sll = __int128_t;
using db = double;
using Pr = pair<ll, ll>;
using Pd = pair<double, double>;
using vi = vector<int>;
using vs = vector<string>;
using vc = vector<char>;
using vl = vector<ll>;
using vb = vector<bool>;
using vd = vector<double>;
using vp = vector<Pr>;
using vpd = vector<Pd>;
using vvi = vector<vector<int>>;
using vvc = vector<vector<char>>;
using vvl = vector<vector<ll>>;
using vvp = vector<vector<Pr>>;
using vvb = vector<vector<bool>>;
using vvd = vector<vector<double>>;
using vvs = vector<vector<string>>;
using vvvi = vector<vector<vector<int>>>;
using vvvl = vector<vector<vector<ll>>>;
using vvvb = vector<vector<vector<bool>>>;
using vvvd = vector<vector<vector<double>>>;
using t3 = tuple<ll,ll,ll>;
using t3d = tuple<db,db,db>;
using t4 = tuple<ll,ll,ll,ll>;
using vt3 = vector<t3>;
using vt3d = vector<t3d>;
using vt4 = vector<t4>;
using vvt3 = vector<vector<t3>>;
using vvt3d = vector<vector<t3d>>;
using vvt4 = vector<vector<t4>>;
using pq = priority_queue<Pr,vector<Pr>,greater<Pr>>;
using cl = complex<ll>;
using cd = complex<double>;
#define rep(i, N) for (ll i=0; i<(ll)(N); ++i)
#define repr(i, N) for (ll i = (ll)(N) - 1; i >= 0; --i)
#define repk(i, k, N) for (ll i = k; i < (ll)(N); ++i)
#define rep1(i, N) for (ll i=1; i<(ll)(N+1); ++i)
#define rep1r(i, N) for (ll i=(ll)(N); i>0; i--)
#define all(v) (v).begin(), (v).end()
#define allr(v) (v).rbegin(), (v).rend()
#define SIZE(v) (ll)((v).size())
#define PYes {puts("Yes"); exit(0);}
#define PNo {puts("No"); exit(0);}
#define PFi {puts("First"); exit(0);}
#define PSe {puts("Second"); exit(0);}
#define Pm0 {puts("0"); exit(0);}
#define Pm1 {puts("-1"); exit(0);}
#define INT(...) int __VA_ARGS__; in(__VA_ARGS__)
#define INTM(...) int __VA_ARGS__; inm(__VA_ARGS__)
#define LONG(...) ll __VA_ARGS__; in(__VA_ARGS__)
#define LONGM(...) ll __VA_ARGS__; inm(__VA_ARGS__)
#define DOUBLE(...) double __VA_ARGS__; in(__VA_ARGS__)
#define CHAR(...) char __VA_ARGS__; in(__VA_ARGS__)
#define STRING(...) string __VA_ARGS__; in(__VA_ARGS__)
#define VI(ivec, n) vi ivec(n); input_ivec(ivec, n)
#define VIM(ivec, n) vi ivec(n); input_ivecm(ivec, n)
#define VL(lvec, n) vl lvec(n); input_lvec(lvec, n)
#define VLM(lvec, n) vl lvec(n); input_lvecm(lvec, n)
#define VL2(lvec1, lvec2, n) vl lvec1(n), lvec2(n); input_lvec12(lvec1, lvec2, n)
#define VL2M(lvec1, lvec2, n) vl lvec1(n), lvec2(n); input_lvec12m(lvec1, lvec2, n)
#define VC(cvec, n) vc cvec(n); input_cvec(cvec, n)
#define VS(svec, n) vs svec(n); input_svec(svec, n)
#define VD(dvec, n) vd dvec(n); input_dvec(dvec, n)
#define VP(pvec, n) vp pvec(n); input_pvec(pvec, n)
#define VPD(pvec, n) vpd pvec(n); input_pvecd(pvec, n)
#define VPM(pvec, n) vp pvec(n); input_pvecm(pvec, n)
#define VVI(ivec2, h, w) vvi ivec2(h, vi(w)); input_ivec2(ivec2, h, w)
#define VVL(lvec2, h, w) vvl lvec2(h, vl(w)); input_lvec2(lvec2, h, w)
#define VVLM(lvec2, h, w) vvl lvec2(h, vl(w)); input_lvec2m(lvec2, h, w)
#define VVC(cvec2, h, w) vvc cvec2(h, vc(w)); input_cvec2(cvec2, h, w)
#define pcnt(x) (ll)__builtin_popcountll(x)
#define parity(x) (ll)__builtin_parityll(x)
#define uset unordered_set
#define umap unordered_map
inline void Out(double x) {printf("%.15f",x);cout<<'\n';}
template<typename T> inline void Out(pair<T,T> x) {cout<<x.first<<' '<<x.second<<'\n';}
template<typename T> inline void Out(T x) {cout<<x<<'\n';}
inline void Out(vector<string> v) {rep(i,SIZE(v)) cout<<v[i]<<'\n';}
template<typename T> inline void Out(queue<T> q){while(!q.empty()) {cout<<q.front()<<" "; q.pop();} cout<<endl;}
template<typename T> inline void Out(deque<T> q){while(!q.empty()) {cout<<q.front()<<" "; q.pop_front();} cout<<endl;}
template<typename T> inline void Out(vector<T> v) {rep(i,SIZE(v)) cout<<v[i]<<(i==SIZE(v)-1?'\n':' ');}
template<typename T> inline void Out(vector<vector<T>> &vv){for(auto &v: vv) Out(v);}
template<typename T> inline void Out(vector<pair<T,T>> v) {for(auto p:v) Out(p);}
template<typename T> inline void Outend(T x) {Out(x); exit(0);}
template<typename T> inline void chmin(T &a, T b) { a = min(a, b); }
template<typename T> inline void chmax(T &a, T b) { a = max(a, b); }
inline void mi(void) {return;}
template<typename T1, typename... T2> void mi(T1& f, T2&... r) {--f; mi(r...);}
template<class... T> void in(T&... x) {(cin >> ... >> x);}
template<class... T> void inm(T&... x) {(cin >> ... >> x); mi(x...);}
inline void input_ivec(vi &ivec, int n) {rep(i, n) {cin>>ivec[i];}}
inline void input_ivecm(vi &ivec, int n) {rep(i, n) {cin>>ivec[i];--ivec[i];}}
inline void input_lvec(vl &lvec, ll n) {rep(i, n) {cin>>lvec[i];}}
inline void input_lvecm(vl &lvec, ll n) {rep(i, n) {cin>>lvec[i];--lvec[i];}}
inline void input_lvec12(vl &lvec1, vl &lvec2, ll n) {rep(i, n) {cin>>lvec1[i]>>lvec2[i];}}
inline void input_lvec12m(vl &lvec1, vl &lvec2, ll n) {rep(i, n) {cin>>lvec1[i]>>lvec2[i];--lvec1[i];--lvec2[i];}}
inline void input_cvec(vc &cvec, ll n) {rep (i, n) {cin>>cvec[i];}}
inline void input_svec(vs &svec, ll n) {rep (i, n) {cin>>svec[i];}}
inline void input_dvec(vd &dvec, ll n) {rep (i, n) {cin>>dvec[i];}}
inline void input_pvec(vp &pvec, ll n) {rep (i, n) {cin>>pvec[i].first>>pvec[i].second;}}
inline void input_pvecm(vp &pvec, ll n) {rep (i, n) {cin>>pvec[i].first>>pvec[i].second;pvec[i].first--,pvec[i].second--;}}
inline void input_pvecd(vpd &pvec, ll n) {rep (i, n) {cin>>pvec[i].first>>pvec[i].second;}}
inline void input_ivec2(vvi &ivec2, int h, int w) {rep(i, h) rep(j, w) {cin>>ivec2[i][j];}}
inline void input_lvec2(vvl &lvec2, ll h, ll w) {rep(i, h) rep(j, w) {cin>>lvec2[i][j];}}
inline void input_lvec2m(vvl &lvec2, ll h, ll w) {rep(i, h) rep(j, w) {cin>>lvec2[i][j];--lvec2[i][j];}}
inline void input_cvec2(vvc &cvec2, ll h, ll w) {rep(i, h) rep(j, w) {cin>>cvec2[i][j];}}
inline bool isin(ll i, ll j, ll h, ll w) {if(i<0||i>=h||j<0||j>=w) return false; else return true;}
template<typename T> inline T TmpPercent(T a, T b) {if(b<0){a=-a,b=-b;} return (a%b+b)%b;}
template<typename T> inline T Percent(T a, T b) {if(b<0) return -TmpPercent(a,b); return TmpPercent(a,b);}
template<typename T> inline T Div(T a, T b) {if(b<0){a=-a,b=-b;} return (a-TmpPercent(a,b))/b; }
template<typename T> inline T Divceil(T a, T b) {if(TmpPercent(a,b)==0) return Div(a,b); return Div(a,b)+1;}
template<typename T> void erase(multiset<T> &st, T x) {if(st.contains(x)) st.erase(st.find(x));}
template<typename T> T pop(vector<T> &x) {T ret=x.back(); x.pop_back(); return ret;}
#ifdef __DEBUG
#define de(var) {cerr << #var << ": "; debug_view(var);}
#define de2(var1,var2) {cerr<<#var1<<' '<<#var2<<": "; debug_view(var1,var2);}
#define de3(var1,var2,var3) {cerr<<#var1<<' '<<#var2<<' '<<#var3<<": "; debug_view(var1,var2,var3);}
#define de4(var1,var2,var3,var4) {cerr<<#var1<<' '<<#var2<<' '<<#var3<<' '<<#var4<<": "; debug_view(var1,var2,var3,var4);}
#define de5(var1,var2,var3,var4,var5) {cerr<<#var1<<' '<<#var2<<' '<<#var3<<' '<<#var4<<' '<<#var5<<": "; debug_view(var1,var2,var3,var4,var5);}
template<typename T> inline void debug_view(T e){cerr << e << endl;}
template<typename T1, typename T2> inline void debug_view(T1 e1, T2 e2){cerr<<e1<<' '<<e2<<endl;}
template<typename T1, typename T2, typename T3> inline void debug_view(T1 e1, T2 e2, T3 e3){cerr<<e1<<' '<<e2<<' '<<e3<<endl;}
template<typename T1, typename T2, typename T3, typename T4> inline void debug_view(T1 e1, T2 e2, T3 e3, T4 e4){cerr<<e1<<' '<<e2<<' '<<e3<<' '<<e4<<endl;}
template<typename T1, typename T2, typename T3, typename T4, typename T5> inline void debug_view(T1 e1, T2 e2, T3 e3, T4 e4, T5 e5){cerr<<e1<<' '<<e2<<' '<<e3<<' '<<e4<<' '<<e5<<endl;}
template<typename T1, typename T2> inline void debug_view(pair<T1,T2> &p){cerr<<"{"<<p.first<<" "<<p.second<<"}\n";}
template<typename T1, typename T2> inline void debug_view(vector<pair<T1,T2>> &v){for(auto [a,b]: v){cerr<<"{"<<a<<" "<<b<<"} ";} cerr << endl;}
template<typename T1, typename T2> inline void debug_view(set<pair<T1,T2>> &s){for(auto [a,b]: s){cerr<<"{"<<a<<" "<<b<<"} ";} cerr << endl;}
template<typename T> inline void debug_view(tuple<T,T,T> t){cerr<<get<0>(t)<<' '<<get<1>(t)<<' '<<get<2>(t)<< endl;}
template<typename T> inline void debug_view(queue<T> q){while(!q.empty()) {cerr << q.front() << " "; q.pop();}cerr << endl;}
template<typename T> inline void debug_view(deque<T> q){while(!q.empty()) {cerr << q.front() << " "; q.pop_front();}cerr << endl;}
template<typename T> inline void debug_view(set<T> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(set<T,greater<T>> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(unordered_set<T> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(multiset<T> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(multiset<T,greater<T>> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(vector<pair<T,T>> &v){for(auto [a,b]: v){cerr<<"{"<<a<<" "<<b<<"} ";} cerr << endl;}
inline void debug_view(vector<string> &v){cerr << "----" << endl; for(auto s: v) debug_view(s);}
template<typename T> inline void debug_view(vector<T> &v){for(auto e: v){cerr << e << " ";} cerr << endl;}
template<typename T> inline void debug_view(vector<vector<pair<T,T>>> &vv){cerr << "----" << endl;for(auto &v: vv){debug_view(v);} cerr << "--------" << endl;}
template<typename T> inline void debug_view(vector<vector<T>> &vv){cerr << "----" << endl;for(auto &v: vv){debug_view(v);} cerr << "--------" << endl;}
template<typename T1,typename T2> inline void debug_view(map<T1,T2> &mp){cerr << "----" << endl;for(auto [k,v]: mp){cerr << k << ' ' << v << endl;} cerr << "--------" << endl;}
template<typename T1,typename T2> inline void debug_view(unordered_map<T1,T2> &mp){cerr << "----" << endl;for(auto [k,v]: mp){cerr << k << ' ' << v << endl;} cerr << "--------" << endl;}
template<typename T1,typename T2> inline void debug_view(map<T1,vector<T2>> &mp){cerr<<"----"<<endl;for(auto [k,v]: mp){cerr<<k<<": ";debug_view(v);} cerr << "--------" << endl;}
template<typename T1,typename T2,typename T3> inline void debug_view(map<pair<T1,T2>,T3> &mp){cerr << "----" << endl;for(auto [p,v]: mp){cerr<<'{'<<p.first<<' '<<p.second<<'}'<<": "<<v<<endl;} cerr<<"--------"<<endl;}
#define deb(var) {cerr << #var << ": "; debugb_view(var);}
template<typename T> inline void debugb_view(T e){bitset<20> b(e); cerr<<b<<endl;}
template<typename T> inline void debugb_view(vector<T> &v){cerr<<"----"<<endl;for(auto e: v){debugb_view(e);}}
#else
#define de(var) {}
#define de2(var1,var2) {}
#define de3(var1,var2,var3) {}
#define de4(var1,var2,var3,var4) {}
#define de5(var1,var2,var3,var4,var5) {}
#define deb(var) {}
#endif
int IINF = 1001001001;
ll INF = 3e18;
const ll M998 = 998244353;
const ll M107 = 1000000007;
template<typename T> inline void ch1(T &x){if(x==INF)x=-1;}
const double PI = acos(-1);
const double EPS = 1e-8;  //eg) if x=1e6, EPS >= 1e6/1e14(=1e-8)
const vi di = {0, 1, 0, -1};
const vi dj = {1, 0, -1, 0};
const vp dij = {{0,1},{1,0},{0,-1},{-1,0}};
const vp hex0 = {{-1,-1},{-1,0},{0,-1},{0,1},{1,-1},{1,0}}; // tobide
const vp hex1 = {{-1,0},{-1,1},{0,-1},{0,1},{1,0},{1,1}};  // hekomi
const vi di8 = {-1, -1, -1, 0, 0, 1, 1, 1};
const vi dj8 = {-1, 0, 1, -1, 1, -1, 0, 1};
const vp dij8 = {{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};
Pr operator+ (Pr a, Pr b) {return {a.first+b.first, a.second+b.second};}
Pr operator- (Pr a, Pr b) {return {a.first-b.first, a.second-b.second};}
Pr operator* (Pr a, Pr b) {return {a.first*b.first, a.second*b.second};}
Pr operator/ (Pr a, Pr b) {return {a.first/b.first, a.second/b.second};}


struct SCC {
    SCC (long long _n): n(_n), from(_n), ifrom(_n) {}
    void add_edge (long long a, long long b) {
        from[a].push_back(b);
        ifrom[b].push_back(a);
    }
    vector<vector<long long>> scc () {
        vector<vector<long long>> group;
        back_num.clear();
        selected.assign(n, false);
        for (long long i=0; i < n; ++i) {
            if (!selected[i]) dfs1(i);
        }
        selected.assign(n, false);
        for (long long i=n-1; i >= 0; --i) {
            long long x = back_num[i];
            if (selected[x]) continue;
            vector<long long> emp;
            dfs2(x, emp);
            group.push_back(emp);
        }
        return group;
    }
private:
    long long n;
    vector<vector<long long>> from, ifrom;
    vector<long long> back_num;
    vector<bool> selected;
    void dfs1 (long long x) {
        selected[x] = true;
        for (auto y: from[x]) {
            if (selected[y]) continue;
            dfs1(y);
        }
        back_num.push_back(x);
    }
    void dfs2 (long long x, vector<long long> &vec) {
        selected[x] = true;
        vec.push_back(x);
        for (auto y: ifrom[x]) {
            if (selected[y]) continue;
            dfs2(y, vec);
        }
    }
};

void solve() {
    LONG(N, M);
    vvl from(N), ifrom(N);
    SCC scc(N);
    rep(i, M) {
        LONG(a, b);
        from[a].emplace_back(b);
        ifrom[b].emplace_back(a);
        scc.add_edge(a,b);
    }

    auto grs = scc.scc();
    if(SIZE(grs)==1) Pm0

    ll ind=0, outd=0;
    for(auto gr: grs) {
        if(SIZE(gr)==1) {
            if(SIZE(from[gr[0]])==0) ++outd;
            if(SIZE(ifrom[gr[0]])==0) ++ind;
            continue;
        }
        uset<ll> st;
        for(auto v: gr) st.insert(v);
        bool inward=false, outward=false;
        for(auto v: gr) {
            for(auto nv: from[v]) {
                if(st.count(nv)) continue;
                outward = true;
            }
            for(auto nv: ifrom[v]) {
                if(st.count(nv)) continue;
                inward = true;
            }
        }
        if(!inward) ++ind;
        if(!outward) ++outd;
    }
    Out(max(ind,outd));


}

int main () {
    // ios::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
}

// ### test.cpp ###
accuracy: 1
time_ms: 40
memory_kb: 10520
final_rank: 16

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define rep(i, n) for(int i=0; i<(n); i++)

int N, M;
int s[100001], t[100001];
vector<int> G[10001];
vector<int> rG[10001];
int deg_in[10001], deg_out[10001];
bool visit[10001];
int idx[10001];

void add_edge(int from, int to){
    G[from].emplace_back(to);
    rG[to].emplace_back(from);
}

void dfs(int pos, vector<int>& vs){
    visit[pos] = true;
    for(int to : G[pos]){
        if(visit[to]) continue;
        dfs(to, vs);
    }
    vs.emplace_back(pos);
}

void rdfs(int pos, int k){
    visit[pos] = true;
    idx[pos] = k;
    for(int to : rG[pos]){
        if(visit[to]) continue;
        rdfs(to, k);
    }
}

void dfs_dag(int pos){
    visit[pos] = true;
    for(int to : G[pos]){
        if(idx[pos] != idx[to]){
            deg_out[idx[pos]]++;
            deg_in[idx[to]]++;
        }
        if(visit[to]) continue;
        dfs_dag(to);
    }
}

int scc(){
    rep(i, N){
        visit[i] = false;
        idx[i] = -1;
    }
    vector<int> vs;
    rep(i, N){
        if(!visit[i]) dfs(i, vs);
    }
    rep(i, N) visit[i] = false;
    int k = 0;
    for(int i=N-1; i>=0; i--){
        if(!visit[vs[i]]) rdfs(vs[i], k++);
    }
    rep(i, N) visit[i] = false;
    rep(i, N) if(!visit[i]) dfs_dag(i);
    return k;
}

int main(){
    cin >> N >> M;
    rep(i, M){
        cin >> s[i] >> t[i];
        add_edge(s[i], t[i]);
    }

    int K = scc();
    int cnt_in = 0, cnt_out = 0;
    rep(i, K){
        if(deg_in[i] == 0) cnt_in++;
        if(deg_out[i] == 0) cnt_out++;
    }

    int ans = max(cnt_in, cnt_out);
    if(K == 1) ans = 0;

    cout << ans << endl;
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 6880
final_rank: 7

code_snippet: ﻿// ### test.cpp ###
#include <bits/stdc++.h>
#ifdef __DEBUG_VECTOR
namespace for_debugging{
    struct subscript_and_location{
        int sub;
        std::source_location loc;
        subscript_and_location(int sub_,std::source_location loc_=std::source_location::current()){
            sub=sub_;
            loc=loc_;
        }
        void check_out_of_range(size_t sz){
            if(sub<0||(int)sz<=sub){
                std::clog << loc.file_name() << ":(" << loc.line() << ":" << loc.column() << "):" << loc.function_name() << std::endl;
                std::clog << "out of range: subscript = " << sub << ", vector_size = " << sz << std::endl;
                exit(EXIT_FAILURE);
            }
        }
    };
}
namespace std{
    template<class T,class Allocator=std::allocator<T>> class vector_for_debugging:public std::vector<T,Allocator>{
        using std::vector<T,Allocator>::vector;
        public:
            [[nodiscard]] constexpr std::vector<T,Allocator>::reference operator[](for_debugging::subscript_and_location n) noexcept(!std::is_same<T,bool>::value){
                n.check_out_of_range(this->size());
                return std::vector<T,Allocator>::operator[](n.sub);
            }
            [[nodiscard]] constexpr std::vector<T,Allocator>::const_reference operator[](for_debugging::subscript_and_location n) const noexcept(!std::is_same<T,bool>::value){
                n.check_out_of_range(this->size());
                return std::vector<T,Allocator>::operator[](n.sub);
            }
    };
    namespace pmr{
        template<class T> using vector_for_debugging=std::vector_for_debugging<T,std::pmr::polymorphic_allocator<T>>;
    }
}
#define vector vector_for_debugging
#endif
using namespace std;
using ll = long long;
using ull = unsigned long long;
using sll = __int128_t;
using db = double;
using Pr = pair<ll, ll>;
using Pd = pair<double, double>;
using vi = vector<int>;
using vs = vector<string>;
using vc = vector<char>;
using vl = vector<ll>;
using vb = vector<bool>;
using vd = vector<double>;
using vp = vector<Pr>;
using vpd = vector<Pd>;
using vvi = vector<vector<int>>;
using vvc = vector<vector<char>>;
using vvl = vector<vector<ll>>;
using vvp = vector<vector<Pr>>;
using vvb = vector<vector<bool>>;
using vvd = vector<vector<double>>;
using vvs = vector<vector<string>>;
using vvvi = vector<vector<vector<int>>>;
using vvvl = vector<vector<vector<ll>>>;
using vvvb = vector<vector<vector<bool>>>;
using vvvd = vector<vector<vector<double>>>;
using t3 = tuple<ll,ll,ll>;
using t3d = tuple<db,db,db>;
using t4 = tuple<ll,ll,ll,ll>;
using vt3 = vector<t3>;
using vt3d = vector<t3d>;
using vt4 = vector<t4>;
using vvt3 = vector<vector<t3>>;
using vvt3d = vector<vector<t3d>>;
using vvt4 = vector<vector<t4>>;
using pq = priority_queue<Pr,vector<Pr>,greater<Pr>>;
using cl = complex<ll>;
using cd = complex<double>;
#define rep(i, N) for (ll i=0; i<(ll)(N); i++)
#define repr(i, N) for (ll i = (ll)(N) - 1; i >= 0; i--)
#define repk(i, k, N) for (ll i = k; i < (ll)(N); i++)
#define rep1(i, N) for (ll i=1; i<(ll)(N+1); i++)
#define all(v) (v).begin(), (v).end()
#define allr(v) (v).rbegin(), (v).rend()
#define SIZE(v) (ll)((v).size())
#define PYes {puts("Yes"); exit(0);}
#define PNo {puts("No"); exit(0);}
#define Pm0 {puts("0"); exit(0);}
#define Pm1 {puts("-1"); exit(0);}
#define INT(...) int __VA_ARGS__; in(__VA_ARGS__)
#define INTM(...) int __VA_ARGS__; inm(__VA_ARGS__)
#define LONG(...) ll __VA_ARGS__; in(__VA_ARGS__)
#define LONGM(...) ll __VA_ARGS__; inm(__VA_ARGS__)
#define DOUBLE(...) double __VA_ARGS__; in(__VA_ARGS__)
#define CHAR(...) char __VA_ARGS__; in(__VA_ARGS__)
#define STRING(...) string __VA_ARGS__; in(__VA_ARGS__)
#define VI(ivec, n) vi ivec(n); input_ivec(ivec, n)
#define VIM(ivec, n) vi ivec(n); input_ivecm(ivec, n)
#define VL(lvec, n) vl lvec(n); input_lvec(lvec, n)
#define VLM(lvec, n) vl lvec(n); input_lvecm(lvec, n)
#define VL2(lvec1, lvec2, n) vl lvec1(n), lvec2(n); input_lvec12(lvec1, lvec2, n)
#define VL2M(lvec1, lvec2, n) vl lvec1(n), lvec2(n); input_lvec12m(lvec1, lvec2, n)
#define VC(cvec, n) vc cvec(n); input_cvec(cvec, n)
#define VS(svec, n) vs svec(n); input_svec(svec, n)
#define VD(dvec, n) vd dvec(n); input_dvec(dvec, n)
#define VP(pvec, n) vp pvec(n); input_pvec(pvec, n)
#define VPD(pvec, n) vpd pvec(n); input_pvecd(pvec, n)
#define VPM(pvec, n) vp pvec(n); input_pvecm(pvec, n)
#define VVI(ivec2, h, w) vvi ivec2(h, vi(w)); input_ivec2(ivec2, h, w)
#define VVL(lvec2, h, w) vvl lvec2(h, vl(w)); input_lvec2(lvec2, h, w)
#define VVLM(lvec2, h, w) vvl lvec2(h, vl(w)); input_lvec2m(lvec2, h, w)
#define VVC(cvec2, h, w) vvc cvec2(h, vc(w)); input_cvec2(cvec2, h, w)
#define pcnt(x) (ll)__builtin_popcountll(x)
#define parity(x) (ll)__builtin_parityll(x)
#define uset unordered_set
#define umap unordered_map
inline void Out(double x) {printf("%.15f",x);cout<<'\n';}
template<typename T> inline void Out(pair<T,T> x) {cout<<x.first<<' '<<x.second<<'\n';}
template<typename T> inline void Out(T x) {cout<<x<<'\n';}
inline void Out(vector<string> v) {rep(i,SIZE(v)) cout<<v[i]<<'\n';}
template<typename T> inline void Out(queue<T> q){while(!q.empty()) {cout<<q.front()<<" "; q.pop();} cout<<endl;}
template<typename T> inline void Out(deque<T> q){while(!q.empty()) {cout<<q.front()<<" "; q.pop_front();} cout<<endl;}
template<typename T> inline void Out(vector<T> v) {rep(i,SIZE(v)) cout<<v[i]<<(i==SIZE(v)-1?'\n':' ');}
template<typename T> inline void Out(vector<vector<T>> &vv){for(auto &v: vv) Out(v);}
template<typename T> inline void Out(vector<pair<T,T>> v) {for(auto p:v) Out(p);}
template<typename T> inline void Outend(T x) {Out(x); exit(0);}
template<typename T> inline void chmin(T &a, T b) { a = min(a, b); }
template<typename T> inline void chmax(T &a, T b) { a = max(a, b); }
inline void mi(void) {return;}
template<typename T1, typename... T2> void mi(T1& f, T2&... r) {--f; mi(r...);}
template<class... T> void in(T&... x) {(cin >> ... >> x);}
template<class... T> void inm(T&... x) {(cin >> ... >> x); mi(x...);}
inline void input_ivec(vi &ivec, int n) {rep(i, n) {cin>>ivec[i];}}
inline void input_ivecm(vi &ivec, int n) {rep(i, n) {cin>>ivec[i];--ivec[i];}}
inline void input_lvec(vl &lvec, ll n) {rep(i, n) {cin>>lvec[i];}}
inline void input_lvecm(vl &lvec, ll n) {rep(i, n) {cin>>lvec[i];--lvec[i];}}
inline void input_lvec12(vl &lvec1, vl &lvec2, ll n) {rep(i, n) {cin>>lvec1[i]>>lvec2[i];}}
inline void input_lvec12m(vl &lvec1, vl &lvec2, ll n) {rep(i, n) {cin>>lvec1[i]>>lvec2[i];--lvec1[i];--lvec2[i];}}
inline void input_cvec(vc &cvec, ll n) {rep (i, n) {cin>>cvec[i];}}
inline void input_svec(vs &svec, ll n) {rep (i, n) {cin>>svec[i];}}
inline void input_dvec(vd &dvec, ll n) {rep (i, n) {cin>>dvec[i];}}
inline void input_pvec(vp &pvec, ll n) {rep (i, n) {cin>>pvec[i].first>>pvec[i].second;}}
inline void input_pvecm(vp &pvec, ll n) {rep (i, n) {cin>>pvec[i].first>>pvec[i].second;pvec[i].first--,pvec[i].second--;}}
inline void input_pvecd(vpd &pvec, ll n) {rep (i, n) {cin>>pvec[i].first>>pvec[i].second;}}
inline void input_ivec2(vvi &ivec2, int h, int w) {rep(i, h) rep(j, w) {cin>>ivec2[i][j];}}
inline void input_lvec2(vvl &lvec2, ll h, ll w) {rep(i, h) rep(j, w) {cin>>lvec2[i][j];}}
inline void input_lvec2m(vvl &lvec2, ll h, ll w) {rep(i, h) rep(j, w) {cin>>lvec2[i][j];--lvec2[i][j];}}
inline void input_cvec2(vvc &cvec2, ll h, ll w) {rep(i, h) rep(j, w) {cin>>cvec2[i][j];}}
inline bool isin(ll i, ll j, ll h, ll w) {if(i<0||i>=h||j<0||j>=w) return false; else return true;}
inline ll TmpPercent(ll a, ll b) {if(b<0){a=-a,b=-b;} return (a%b+b)%b;}
inline ll Percent(ll a, ll b) {if(b<0) return -TmpPercent(a,b); return TmpPercent(a,b);}
inline ll Div(ll a, ll b) {if(b<0){a=-a,b=-b;} return (a-TmpPercent(a,b))/b; }
inline ll Divceil(ll a, ll b) {if(TmpPercent(a,b)==0) return Div(a,b); return Div(a,b)+1;}
#ifdef __DEBUG
#define de(var) {cerr << #var << ": "; debug_view(var);}
#define de2(var1,var2) {cerr<<#var1<<' '<<#var2<<": "; debug_view(var1,var2);}
#define de3(var1,var2,var3) {cerr<<#var1<<' '<<#var2<<' '<<#var3<<": "; debug_view(var1,var2,var3);}
#define de4(var1,var2,var3,var4) {cerr<<#var1<<' '<<#var2<<' '<<#var3<<' '<<#var4<<": "; debug_view(var1,var2,var3,var4);}
template<typename T> inline void debug_view(T e){cerr << e << endl;}
template<typename T1, typename T2> inline void debug_view(T1 e1, T2 e2){cerr<<e1<<' '<<e2<<endl;}
template<typename T1, typename T2, typename T3> inline void debug_view(T1 e1, T2 e2, T3 e3){cerr<<e1<<' '<<e2<<' '<<e3<<endl;}
template<typename T1, typename T2, typename T3, typename T4> inline void debug_view(T1 e1, T2 e2, T3 e3, T4 e4){cerr<<e1<<' '<<e2<<' '<<e3<<' '<<e4<<endl;}
template<typename T1, typename T2> inline void debug_view(pair<T1,T2> &p){cerr<<"{"<<p.first<<" "<<p.second<<"}\n";}
template<typename T1, typename T2> inline void debug_view(vector<pair<T1,T2>> &v){for(auto [a,b]: v){cerr<<"{"<<a<<" "<<b<<"} ";} cerr << endl;}
template<typename T1, typename T2> inline void debug_view(set<pair<T1,T2>> &s){for(auto [a,b]: s){cerr<<"{"<<a<<" "<<b<<"} ";} cerr << endl;}
template<typename T> inline void debug_view(tuple<T,T,T> t){cerr<<get<0>(t)<<' '<<get<1>(t)<<' '<<get<2>(t)<< endl;}
template<typename T> inline void debug_view(queue<T> q){while(!q.empty()) {cerr << q.front() << " "; q.pop();}cerr << endl;}
template<typename T> inline void debug_view(deque<T> q){while(!q.empty()) {cerr << q.front() << " "; q.pop_front();}cerr << endl;}
template<typename T> inline void debug_view(set<T> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(set<T,greater<T>> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(unordered_set<T> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(multiset<T> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(multiset<T,greater<T>> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(vector<pair<T,T>> &v){for(auto [a,b]: v){cerr<<"{"<<a<<" "<<b<<"} ";} cerr << endl;}
inline void debug_view(vector<string> &v){cerr << "----" << endl; for(auto s: v) debug_view(s);}
template<typename T> inline void debug_view(vector<T> &v){for(auto e: v){cerr << e << " ";} cerr << endl;}
template<typename T> inline void debug_view(vector<vector<pair<T,T>>> &vv){cerr << "----" << endl;for(auto &v: vv){debug_view(v);} cerr << "--------" << endl;}
template<typename T> inline void debug_view(vector<vector<T>> &vv){cerr << "----" << endl;for(auto &v: vv){debug_view(v);} cerr << "--------" << endl;}
template<typename T1,typename T2> inline void debug_view(map<T1,T2> &mp){cerr << "----" << endl;for(auto [k,v]: mp){cerr << k << ' ' << v << endl;} cerr << "--------" << endl;}
template<typename T1,typename T2> inline void debug_view(unordered_map<T1,T2> &mp){cerr << "----" << endl;for(auto [k,v]: mp){cerr << k << ' ' << v << endl;} cerr << "--------" << endl;}
template<typename T1,typename T2> inline void debug_view(map<T1,vector<T2>> &mp){cerr<<"----"<<endl;for(auto [k,v]: mp){cerr<<k<<": ";debug_view(v);} cerr << "--------" << endl;}
template<typename T1,typename T2,typename T3> inline void debug_view(map<pair<T1,T2>,T3> &mp){cerr << "----" << endl;for(auto [p,v]: mp){cerr<<'{'<<p.first<<' '<<p.second<<'}'<<": "<<v<<endl;} cerr<<"--------"<<endl;}
#define deb(var) {cerr << #var << ": "; debugb_view(var);}
template<typename T> inline void debugb_view(T e){bitset<20> b(e); cerr<<b<<endl;}
template<typename T> inline void debugb_view(vector<T> &v){cerr<<"----"<<endl;for(auto e: v){debugb_view(e);}}
#else
#define de(var) {}
#define de2(var1,var2) {}
#define de3(var1,var2,var3) {}
#define de4(var1,var2,var3,var4) {}
#define deb(var) {}
#endif
ll INF = 3e18;
const ll M998 = 998244353;
const ll M107 = 1000000007;
template<typename T> inline void ch1(T &x){if(x==INF)x=-1;}
const double PI = acos(-1);
const double EPS = 1e-8;  //eg) if x=1e9, EPS >= 1e9/1e15(=1e-6)
const vi di = {0, 1, 0, -1};
const vi dj = {1, 0, -1, 0};
const vp dij = {{0,1},{1,0},{0,-1},{-1,0}};
const vi di8 = {-1, -1, -1, 0, 0, 1, 1, 1};
const vi dj8 = {-1, 0, 1, -1, 1, -1, 0, 1};
Pr operator+ (Pr a, Pr b) {return {a.first+b.first, a.second+b.second};}
Pr operator- (Pr a, Pr b) {return {a.first-b.first, a.second-b.second};}
Pr operator* (Pr a, Pr b) {return {a.first*b.first, a.second*b.second};}
Pr operator/ (Pr a, Pr b) {return {a.first/b.first, a.second/b.second};}
struct SCC {
    SCC (long long _n): n(_n), from(_n), ifrom(_n) {}
    void add_edge (long long a, long long b) {
        from[a].push_back(b);
        ifrom[b].push_back(a);
    }
    vector<vector<long long>> scc () {
        vector<vector<long long>> group;
        back_num.clear();
        selected.assign(n, false);
        for (long long i=0; i < n; ++i) {
            if (!selected[i]) dfs1(i);
        }
        selected.assign(n, false);
        for (long long i=n-1; i >= 0; --i) {
            long long x = back_num[i];
            if (selected[x]) continue;
            vector<long long> emp;
            dfs2(x, emp);
            group.push_back(emp);
        }
        return group;
    }
private:
    long long n;
    vector<vector<long long>> from, ifrom;
    vector<long long> back_num;
    vector<bool> selected;
    void dfs1 (long long x) {
        selected[x] = true;
        for (auto y: from[x]) {
            if (selected[y]) continue;
            dfs1(y);
        }
        back_num.push_back(x);
    }
    void dfs2 (long long x, vector<long long> &vec) {
        selected[x] = true;
        vec.push_back(x);
        for (auto y: ifrom[x]) {
            if (selected[y]) continue;
            dfs2(y, vec);
        }
    }
};


int main () {
    // ios::sync_with_stdio(false);
    cin.tie(nullptr);
    LONG(N, M);
    vvl from(N), ifrom(N);
    SCC scc(N);
    rep(i, M) {
        LONG(a,b);
        from[a].push_back(b);
        ifrom[b].push_back(a);
        scc.add_edge(a,b);
    }

    auto grs = scc.scc();
    if(SIZE(grs)==1) Pm0

    vb used(N);
    ll outnum = 0, innum = 0;
    for(auto gr: grs) {
        for(auto v: gr) used[v] = true;
        bool indeg=false, outdeg=false;
        for(auto v: gr) {
            for(auto nv: from[v]) {
                if(used[nv]) continue;
                outdeg=true;
            }
        }
        for(auto v: gr) {
            for(auto nv: ifrom[v]) {
                if(used[nv]) continue;
                indeg=true;
            }
        }
        if(!outdeg) outnum++;
        if(!indeg) innum++;
        for(auto v: gr) used[v] = false;
        de(gr)
        de2(outdeg, indeg)
    }
    de2(outnum, innum)

    Out(max(outnum, innum));
    
}

// ### test.cpp ###
accuracy: 1
time_ms: 30
memory_kb: 10368
final_rank: 11

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <utility>

using namespace std;
using ll = long long;
using info = pair<int, int>;

#define rep(i, N) for (int i = 0; i < N; ++i)
#define maxn 10009
#define maxe 100009

int mark[maxn];
vector<int> g[maxn], rg[maxn];
vector<int> Eus(maxe), Ev(maxe);

void dfs(int v, vector<int> *g, vector<int> &vec)
{
    mark[v] = 1;
    for (auto u : g[v])
        if (!mark[u])
            dfs(u, g, vec);
    vec.push_back(v);
}

int main()
{
    int n, m;
    cin >> n >> m;
    rep(i, m)
    {
        int s, t;
        cin >> s >> t;
        Eus[i] = s;
        Ev[i] = t;
        g[s].push_back(t);
        rg[t].push_back(s);
    }
    // step1 sccを行う
    vector<int> alls;
    for (int i = 0; i < n; i++)
    {
        if (!mark[i])
            dfs(i, g, alls);
    }
    memset(mark, 0, sizeof mark);
    std::reverse(alls.begin(), alls.end());
    int gp = 1;
    for (auto v : alls)
    {
        if (mark[v])
            continue;
        vector<int> comp;
        dfs(v, rg, comp);
        for (int g : comp)
        {
            mark[g] = gp;
        }
        gp++;
    }
    /*rep(i, n) cout << mark[i] << " ";
    cout << endl; */
    if (gp == 2)
    {
        cout << 0 << endl;
        return 0;
    }
    // step2 新しい有向グラフを構成する(ただし、入次数と出次数のみ管理)
    vector<info> sugar_newG(gp, info{0, 0});
    rep(i, m)
    {
        int u = Eus[i], v = Ev[i];
        if (mark[u] == mark[v])
            continue;
        sugar_newG[mark[u]].first++;
        sugar_newG[mark[v]].second++;
    }
    ll indig = 0, outdig = 0;
    for (int i = 1; i < gp; ++i)
    {
        if (sugar_newG[i].first == 0)
            outdig++;
        if (sugar_newG[i].second == 0)
            indig++;
    }
    cout << max(indig, outdig) << endl;
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 6704
final_rank: 6

code_snippet: #pragma region Macros
 
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,fma,abm,mmx,avx,avx2,popcnt")
 
#include <bits/extc++.h>
// #include <atcoder/all>
// using namespace atcoder;
using namespace std;
using namespace __gnu_pbds;
 
// #include <boost/multiprecision/cpp_dec_float.hpp>
// #include <boost/multiprecision/cpp_int.hpp>
// namespace mp = boost::multiprecision;
// using Bint = mp::cpp_int;
// using Bdouble = mp::number<mp::cpp_dec_float<256>>;
// Bdouble Beps = 0.00000000000000000000000000000001; // 1e-32
// const bool equals(Bdouble a, Bdouble b) { return mp::fabs(a - b) < Beps; }

#define pb emplace_back
#define int ll
#define endl '\n'
 
#define sqrt __builtin_sqrtl
#define cbrt __builtin_cbrtl
#define hypot __builtin_hypotl
 
// #define next asdnext
// #define prev asdprev
 
using ll = long long;
using ld = long double;
const ld PI = acosl(-1);
const int INF = 1 << 30;
const ll INFL = 1LL << 61;
const int MOD = 998244353;
// const int MOD = 1000000007;
 
const ld EPS = 1e-10;
const bool equals(ld a, ld b) { return fabs((a) - (b)) < EPS; }
 
const vector<int> dx = {0, 1, 0, -1, 1, 1, -1, -1}; // → ↓ ← ↑ ↘ ↙ ↖ ↗
const vector<int> dy = {1, 0, -1, 0, 1, -1, -1, 1};
 
#define EC int
struct Edge {
    int from, to;
    EC cost;
    Edge() : from(-1), to(-1), cost(-1) {}
    Edge(int to, EC cost) : to(to), cost(cost) {}
    Edge(int from, int to, EC cost) : from(from), to(to), cost(cost) {}
    bool operator ==(const Edge& e) {
        return this->from == e.from && this->to == e.to && this->cost == e.cost;
    }
    bool operator !=(const Edge& e) {
        return this->from != e.from or this->to != e.to or this->cost != e.cost;
    }
    bool operator <(const Edge& e) { return this->cost < e.cost; }
    bool operator >(const Edge& e) { return this->cost > e.cost; }
};
 
chrono::system_clock::time_point start;
__attribute__((constructor))
void constructor() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(10);
    start = chrono::system_clock::now();
}
 
random_device seed_gen;
mt19937_64 rng(seed_gen());
uniform_int_distribution<int> dist_x(0, 1e9);
struct RNG {
    unsigned Int() {
        return dist_x(rng);
    }
    unsigned Int(unsigned l, unsigned r) {
        return dist_x(rng) % (r - l + 1) + l;
    }
    ld Double() {
        return ld(dist_x(rng)) / 1e9;
    }
} rnd;

namespace bit_function {
    using i64 = ll;
    // using i64 = uint64_t;
    // bit演算, x==0の場合は例外処理した方がよさそう. 区間は [l, r)
    i64 lrmask(int l, int r) { return (1LL << r) - (1LL << l); }
    i64 sub_bit(i64 x, int l, int r) { i64 b = x & ((1LL << r) - (1LL << l)); return b >> l; } // r溢れ可
    i64 bit_width(i64 x) { return 64 - __builtin_clzll(x) + (x == 0); }
    
    i64 popcount(i64 x) { return __builtin_popcountll(x); }
    i64 popcount(i64 x, int l, int r) { return __builtin_popcountll(sub_bit(x, l, r)); }
    i64 unpopcount(i64 x) { return bit_width(x) - __builtin_popcountll(x); } // 最上位bitより下のみ
    i64 unpopcount(i64 x, int l, int r) { return r - l - __builtin_popcountll(sub_bit(x, l, r)); } // 最上位bitより上も含まれうる
    bool is_pow2(i64 x) { return __builtin_popcountll(x) == 1; } // xが負のときは常にfalse
    bool is_pow4(i64 x) { return __builtin_popcountll(x) == 1 && __builtin_ctz(x) % 2 == 0; }
    //bool is_pow4(ll x) { return __builtin_popcountll(x) == 1 && (x&0x55555555); }
    
    int top_bit(i64 x) { return 63 - __builtin_clzll(x);} // 2^kの位 (x > 0)
    int bot_bit(i64 x) { return __builtin_ctzll(x);} // 2^kの位 (x > 0)
    int next_bit(i64 x, int k) { // upper_bound
        x >>= (k + 1);
        int pos = k + 1;
        while (x > 0) {
            if (x & 1) return pos;
            x >>= 1;
            pos++;
        }
        return -1;
    }
    int prev_bit(i64 x, int k) {
        // k = min(k, bit_width(x)); ?
        int pos = 0;
        while (x > 0 && pos < k) {
            if (x & 1) {
                if (pos < k) return pos;
            }
            x >>= 1;
            pos++;
        }
        return -1;
    }
    int kth_bit(i64 x, int k) { // kは1-indexed
        int pos = 0, cnt = 0;
        while (x > 0) {
            if (x & 1) {
                cnt++;
                if (cnt == k) return pos;
            }
            x >>= 1;
            pos++;
        }
        return -1;
    }
    i64 msb(i64 x) { if (x == 0) return 0; return 1LL << (63 - __builtin_clzll(x)); } // mask
    i64 lsb(i64 x) { return (x & -x); } // mask
    
    int countl_zero(i64 x) { return __builtin_clzll(x); }
    int countl_one(i64 x) { // countl_oneと定義が異なるので注意
        i64 ret = 0, k = 63 - __builtin_clzll(x);
        while (k != -1 && (x & (1LL << k))) { k--; ret++; }
        return ret;
    }
    int countr_zero(i64 x) { return __builtin_ctzll(x); } // x=0のとき64
    int countr_one(i64 x) { int ret = 0; while (x & 1) { x >>= 1; ret++; } return ret; }
    // int countr_one(ll x) { return __builtin_popcount(x ^ (x & -~x));

    i64 l_one(i64 x) { // 最上位で連なってる1のmask
        if (x == 0) return 0;
        i64 ret = 0, k = 63 - __builtin_clzll(x);
        while (k != -1 && (x & (1LL << k))) { ret += 1LL << k; k--; }
        return ret;
    }
    
    int floor_log2(i64 x) { return 63 - __builtin_clzll(x); } // top_bit
    int ceil_log2(i64 x) { return 64 - __builtin_clzll(x - 1); }
    i64 bit_floor(i64 x) { if (x == 0) return 0; return 1LL << (63 - __builtin_clzll(x)); } // msb
    i64 bit_ceil(i64 x) { if (x == 0) return 0; return 1LL << (64 - __builtin_clzll(x - 1)); }
    
    i64 rotl(i64 x, int k) { // 有効bit内でrotate. オーバーフロー注意
        i64 w = bit_width(x); k %= w;
        return ((x << k) | (x >> (w - k))) & ((1LL << w) - 1);
    }
    // i64 rotl(i64 x, i64 l, i64 m, i64 r) {}
    i64 rotr(i64 x, int k) {
        i64 w = bit_width(x); k %= w;
        return ((x >> k) | (x << (w - k))) & ((1LL << w) - 1);
    }
    // i64 rotr(i64 x, i64 l, i64 m, i64 r) {}
    i64 bit_reverse(i64 x) { // 有効bit内で左右反転
        i64 r = 0, w = bit_width(x);
        for (i64 i = 0; i < w; i++) r |= ((x >> i) & 1) << (w - i - 1);
        return r;
    }
    // i64 bit_reverse(i64 x, int l, int r) {}
    
    bool is_palindrome(i64 x) { return x == bit_reverse(x); }
    bool is_palindrome(i64 x, int l, int r) { i64 b = sub_bit(x, l, r); return b == bit_reverse(b); }
    i64 concat(i64 a, i64 b) { return (a << bit_width(b)) | b; } // オーバーフロー注意
    i64 erase(i64 x, int l, int r) { return x >> r << l | x & ((1LL << l) - 1); } // [l, r) をカット
    
    i64 hamming(i64 a, i64 b) { return __builtin_popcountll(a ^ b); }
    i64 hamming(i64 a, i64 b, int l, int r) { return __builtin_popcountll(sub_bit(a, l, r) ^ sub_bit(b, l, r)); }
    i64 compcount(i64 x) { return (__builtin_popcountll(x ^ (x >> 1)) + (x & 1)) / 2; }
    i64 compcount2(i64 x) { return compcount(x & (x >> 1)); } // 長さ2以上の連結成分の個数
    i64 adjacount(i64 x) { return __builtin_popcountll(x & (x >> 1)); } // 隣接する1のペアの個数
    
    i64 next_combination(i64 x) {
        i64 t = x | (x - 1); return (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctzll(x) + 1));
    }
} using namespace bit_function;

namespace util_function {
    namespace Std = std;
    __int128_t POW(__int128_t x, int n) {
        __int128_t ret = 1;
        assert(n >= 0);
        if (x == 1 or n == 0) ret = 1;
        else if (x == -1 && n % 2 == 0) ret = 1; 
        else if (x == -1) ret = -1; 
        else if (n % 2 == 0) {
            // assert(x < INFL);
            ret = POW(x * x, n / 2);
        } else {
            // assert(x < INFL);
            ret = x * POW(x, n - 1);
        }
        return ret;
    }
    int per(int x, int y) { // x = qy + r (0 <= r < y) を満たすq
        assert(y != 0);
        if (x >= 0 && y > 0) return x / y;
        if (x >= 0 && y < 0) return x / y - (x % y < 0);
        if (x < 0 && y < 0) return x / y + (x % y < 0);
        return x / y - (x % y < 0); //  (x < 0 && y > 0) 
    }
    int mod(int x, int y) { // x = qy + r (0 <= r < y) を満たすr
        assert(y != 0);
        return x - y * per(x, y);
    } // https://yukicoder.me/problems/no/2781
    int floor(int x, int y) { // (ld)x / y 以下の最大の整数
        assert(y != 0);
        if (y < 0) x = -x, y = -y;
        return x >= 0 ? x / y : (x + 1) / y - 1;
    }
    int ceil(int x, int y) { // (ld)x / y 以上の最小の整数
        assert(y != 0);
        if (y < 0) x = -x, y = -y;
        return x > 0 ? (x - 1) / y + 1 : x / y;
    }
    int round(int x, int y) { // (ld)(x/y)を小数点第1位について四捨五入
        assert(y != 0);
        return (x * 2 + y) / (y * 2);
    }
    int round(int x, int y, int k) { // (ld)(x/y)を10^kの位に関して四捨五入
        assert(y != 0 && k >= 0);
        if (k == 0) return (x * 2 + y) / (y * 2);
        x /= y * POW(10, k - 1);
        if (x % 10 >= 5) return (x + 10 - x % 10) * POW(10, k - 1);
        return x * POW(10, k - 1);
    }
    int round2(int x, int y) { // 五捨五超入 // 未verify
        assert(y != 0);
        if (y < 0) y = -y, x = -x;
        int z = x / y;
        if ((z * 2 + 1) * y <= y * 2) z++;
        return z;
    }
    ld round(ld x, int k) { // xを10^kの位に関して四捨五入. to_string(x, k)優先
        // x += EPS;
        ld d = pow(10, -k);
        return Std::round(x * d) / d;
    }
    ld floor(ld x, int k) { // xを10^kの位に関してflooring
        // x += EPS;
        ld d = pow(10, -k);
        return Std::floor(x * d) / d; // 未verify
    }
    ld ceil(ld x, int k) { // xを10^kの位に関してceiling
        // x -= EPS;
        ld d = pow(10, -k);
        return Std::ceil(x * d) / d; // 未verify
    }
    // int kth(int x, int y, int k) { // x / yの10^kの位の桁
    // }
    int floor(ld x, ld y) { // 誤差対策TODO
        assert(!equals(y, 0));
        return Std::floor(x / y);
        // floor(x) = ceil(x - 1) という話も
    }
    int ceil(ld x, ld y) { // 誤差対策TODO // ceil(p/q) = -floor(-(p/q))らしい
        assert(!equals(y, 0));
        return Std::ceil(x / y);
        // ceil(x) = floor(x + 1)
    }
    int perl(ld x, ld y) { // x = qy + r (0 <= r < y, qは整数) を満たす q
        // 未verify. 誤差対策TODO. EPS外してもいいかも。
        assert(!equals(y, 0));
        if (x >= 0 && y > 0) return Std::floor(x / y)+EPS;
        if (x >= 0 && y < 0) return -Std::floor(x / fabs(y));
        if (x < 0 && y < 0) return Std::floor(x / y) + (x - Std::floor(x/y)*y < -EPS);
        return Std::floor(x / y) - (x - Std::floor(x/y)*y < -EPS); //  (x < 0 && y > 0) 
    }
    ld modl(ld x, ld y) { // x = qy + r (0 <= r < y, qは整数) を満たす r
        // 未verify. 誤差対策TODO. -0.0が返りうる。
        assert(!equals(y, 0));
        if (x >= 0) return x - fabs(y)*fabs(per(x, y));
        return x - fabs(y)*floor(x, fabs(y));
    }
    int seisuu(ld x) { return (int)x; } // 整数部分. 誤差対策TODO
    int modf(ld x) {
        if (x < 0) return ceill(x);
        else return floorl(x);
    }
    // 正なら+EPS, 負なら-EPSしてから、文字列に直して小数点以下を捨てる?
    int seisuu(int x, int y) {
        assert(y != 0);
        return x / y;
    }
    int seisuu(ld x, ld y) { // 誤差対策TODO
        assert(!equals(y, 0));
        return (int)(x / y);
    }

    int floor_log(int base, int x) {
        assert(base >= 2);
        int ret = 0, now = 1;
        while (now <= x) {
            now *= base;
            if (now <= x) ret++;
        }
        return ret;
    }
    int ceil_log(int base, int x) {
        assert(base >= 2);
        int ret = 0, now = 1;
        while (now < x) {
            now *= base;
            ret++;
        }
        return ret;
    }

    template <class T> pair<T, T> max(const pair<T, T> &a, const pair<T, T> &b) {
        if (a.first > b.first or a.first == b.first && a.second > b.second) return a;
        return b;
    }
    template <class T> pair<T, T> min(const pair<T, T> &a, const pair<T, T> &b) {
        if (a.first < b.first or a.first == b.first && a.second < b.second) return a;
        return b;
    }
    
    template <class T> bool chmax(T &a, const T& b) {
        if (a < b) { a = b; return true; } return false;
    }
    template <class T> bool chmin(T &a, const T& b) {
        if (a > b) { a = b; return true; } return false;
    }
    template <class T> T mid(T a, T b, T c) { // 誤差対策TODO
        return a + b + c - Std::max({a, b, c}) - Std::min({a, b, c});
    }
    template <class T> void Sort(T &a, T &b, bool rev = false) { 
        if (rev == false) if (a > b) swap(a, b);
        else if (b > a) swap(b, a);
    }
    template <typename T, typename... Args>
    void Sort(T& a, T& b, T& c, Args&... args) {
        vector<T> vec = {a, b, c, args...};
        sort(vec.begin(), vec.end());
        auto it = vec.begin();
        a = *it++; b = *it++; c = *it++;
        int dummy[] = { (args = *it++, 0)... };
        static_cast<void>(dummy);
    }
    template <typename T, typename... Args>
    void Sortr(T& a, T& b, T& c, Args&... args) {
        vector<T> vec = {a, b, c, args...};
        sort(vec.rbegin(), vec.rend());
        auto it = vec.begin();
        a = *it++; b = *it++; c = *it++;
        int dummy[] = { (args = *it++, 0)... };
        static_cast<void>(dummy);
    }

    istream &operator >>(istream &is, __int128_t& x) {
        string S; is >> S;
        __int128_t ret = 0;
        int f = 1;
        if (S[0] == '-') f = -1; 
        for (int i = 0; i < S.length(); i++)
            if ('0' <= S[i] && S[i] <= '9')
                ret = ret * 10 + S[i] - '0';
        x = ret * f;
        return (is);
    }
    ostream &operator <<(ostream &os, __int128_t x) {
        ostream::sentry s(os);
        if (s) {
            __uint128_t tmp = x < 0 ? -x : x;
            char buffer[128]; char *d = end(buffer);
            do {
                --d; *d = "0123456789"[tmp % 10]; tmp /= 10;
            } while (tmp != 0);
            if (x < 0) { --d; *d = '-'; }
            int len = end(buffer) - d;
            if (os.rdbuf()->sputn(d, len) != len) os.setstate(ios_base::badbit);
        }
        return os;
    }
    
    __int128_t stoll(string &S) {
        __int128_t ret = 0; int f = 1;
        if (S[0] == '-') f = -1; 
        for (int i = 0; i < S.length(); i++)
            if ('0' <= S[i] && S[i] <= '9') ret = ret * 10 + S[i] - '0';
        return ret * f;
    }
    __int128_t gcd(__int128_t a, __int128_t b) { return b ? gcd(b, a % b) : a; }
    __int128_t lcm(__int128_t a, __int128_t b) {
        return a / gcd(a, b) * b;
        // lcmが__int128_tに収まる必要あり
    }
    
    string to_string(ld x, int k) { // xの小数第k位までをstring化する
        assert(k >= 0);
        stringstream ss;
        ss << setprecision(k + 2) << x;
        string s = ss.str();
        if (s.find('.') == string::npos) s += '.';
        int pos = s.find('.');
        for (int i = 0; k >= (int)s.size() - 1 - pos; i++) s += '0';
        s.pop_back();
        if (s.back() == '.') s.pop_back();
        return s;
    
        // stringstream ss; // 第k+1位を四捨五入して第k位まで返す
        // ss << setprecision(k + 1) << x;
        // string s = ss.str();
        // if (s.find('.') == string::npos) s += '.';
        // int pos = s.find('.');
        // for (int i = 0; k > (int)s.size() - 1 - pos; i++) s += '0';
        // if (s.back() == '.') s.pop_back();
        // return s;
    }
    string to_string(__int128_t x) {
        string ret = "";
        if (x < 0) { ret += "-"; x *= -1; }
        while (x) { ret += (char)('0' + x % 10); x /= 10; }
        reverse(ret.begin(), ret.end());
        return ret;
    }
    string to_string(char c) { string s = ""; s += c; return s; }
} using namespace util_function;

struct custom_hash {
    static uint64_t splitmix64(uint64_t x) {
        x += 0x9e3779b97f4a7c15;
        x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
        x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
        return x ^ (x >> 31);
    }
 
    size_t operator()(uint64_t x) const {
        static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
        return splitmix64(x + FIXED_RANDOM);
    }
};

template<class T> size_t HashCombine(const size_t seed,const T &v) {
    return seed^(hash<T>()(v)+0x9e3779b9+(seed<<6)+(seed>>2));
}
template<class T,class S> struct hash<pair<T,S>>{
    size_t operator()(const pair<T,S> &keyval) const noexcept {
        return HashCombine(hash<T>()(keyval.first), keyval.second);
    }
};
template<class T> struct hash<vector<T>>{
    size_t operator()(const vector<T> &keyval) const noexcept {
        size_t s=0;
        for (auto&& v: keyval) s=HashCombine(s,v);
        return s;
    }
};
template<int N> struct HashTupleCore{
    template<class Tuple> size_t operator()(const Tuple &keyval) const noexcept{
        size_t s=HashTupleCore<N-1>()(keyval);
        return HashCombine(s,get<N-1>(keyval));
    }
};
template <> struct HashTupleCore<0>{
    template<class Tuple> size_t operator()(const Tuple &keyval) const noexcept{ return 0; }
};
template<class... Args> struct hash<tuple<Args...>>{
    size_t operator()(const tuple<Args...> &keyval) const noexcept {
        return HashTupleCore<tuple_size<tuple<Args...>>::value>()(keyval);
    }
};
 
#pragma endregion

template <class E> struct csr {
    vector<int> start;
    vector<E> elist;
    explicit csr(int n, const vector<pair<int, E>> &edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

struct _scc_graph {
public:
    explicit _scc_graph(int n) : _n(n) {}

    int num_vertices() { return _n; }

    void add_edge(int from, int to) { edges.push_back({from, {to}}); }

    pair<int, vector<int>> scc_ids() {
        auto g = csr<edge>(_n, edges);
        int now_ord = 0, group_num = 0;
        vector<int> visited, low(_n), ord(_n, -1), ids(_n);
        visited.reserve(_n);
        auto dfs = [&](auto self, int v) -> void {
            low[v] = ord[v] = now_ord++;
            visited.push_back(v);
            for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                auto to = g.elist[i].to;
                if (ord[to] == -1) {
                    self(self, to);
                    low[v] = min(low[v], low[to]);
                } else {
                    low[v] = min(low[v], ord[to]);
                }
            }
            if (low[v] == ord[v]) {
                while (true) {
                    int u = visited.back();
                    visited.pop_back();
                    ord[u] = _n;
                    ids[u] = group_num;
                    if (u == v) break;
                }
                group_num++;
            }
        };
        for (int i = 0; i < _n; i++) {
            if (ord[i] == -1) dfs(dfs, i);
        }
        for (auto& x : ids) {
            x = group_num - 1 - x;
        }
        return {group_num, ids};
    }

    vector<vector<int>> scc() {
        ids = scc_ids();
        int group_num = ids.first;
        vector<int> counts(group_num);
        for (auto x : ids.second) counts[x]++;
        vector<vector<int>> groups(ids.first);
        for (int i = 0; i < group_num; i++) {
            groups[i].reserve(counts[i]);
        }
        for (int i = 0; i < _n; i++) {
            groups[ids.second[i]].push_back(i);
        }
        return groups;
    }

public:
    int _n;
    pair<int, vector<int>> ids;
private:
    struct edge {
        int to;
    };
    vector<pair<int, edge>> edges;
};

struct scc_graph {
public:
    scc_graph() : internal(0) {}
    explicit scc_graph(int n) : internal(n) {}

    void add_edge(int from, int to) {
        int n = internal.num_vertices();
        assert(0 <= from && from < n);
        assert(0 <= to && to < n);
        internal.add_edge(from, to);
        E.pb(make_pair(from, to));
    }

    vector<vector<int>> DAG() {
        int new_n = internal.ids.first; // 圧縮後の頂点数

        scc_id();
        
        vector<vector<int>> ret(new_n);
        indeg.resize(new_n);
        outdeg.resize(new_n);
        for (int i = 0; i < (int)E.size(); i++) {
            int u = E[i].first;
            int v = E[i].second;
            if (u == v) continue;
            if (ids[u] != ids[v]) {
                if (mp[minmax(ids[u], ids[v])]) continue;
                ret[ids[u]].pb(ids[v]);
                indeg[ids[v]]++;
                outdeg[ids[u]]++;
                mp[minmax(ids[u], ids[v])]++;
            }
        }
        return ret;
    }

    vector<vector<int>> scc() { return internal.scc(); }

    vector<int> scc_id() {
        ids = internal.ids.second;
        return ids; 
    }

    vector<vector<int>> rev(const vector<vector<int>> &G) {
        int N = G.size();
        vector<vector<int>> G2(N);
        for (int i = 0; i < N; i++) {
            for (auto nv : G[i]) {
                G2[nv].pb(i);
            }
        }
        return G2;
    }

public:
    vector<pair<int, int>> E;
    vector<int> indeg, outdeg; // 再構築したグラフの入次数, 出次数
private:
    _scc_graph internal;
    vector<int> ids;
    unordered_map<pair<int, int>, int> mp;
};

signed main() {
    int N, M;
    cin >> N >> M;

    scc_graph G(N);
    for (int i = 0; i < M; i++) {
        int u, v;
        cin >> u >> v;
        // u--; v--;
        G.add_edge(u, v);
    }

    // 各強連結成分の頂点集合(Sはトポロジカル順)
    vector<vector<int>> S = G.scc();

    // 強連結成分の個数
  	int k = S.size();

    if (k == 1) {
        cout << 0 << endl;
        return 0;
    }

    // scc[i] := 頂点iの属す強連結成分の番号
    vector<int> scc = G.scc_id();
    
    // 強連結成分ごとに押しつぶしたグラフ(DAG)
    vector<vector<int>> G2 = G.DAG();

    int a = 0, b = 0;

    for (auto &x : G.indeg) {
        if (x == 0) a++;
    }
    for (auto &x : G.outdeg) {
        if (x == 0) b++;
    }
    cout << max(a, b) << endl;
}
accuracy: 1
time_ms: 20
memory_kb: 14516
final_rank: 12

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct SCC {
	long long n;
	vector<vector<pair<long long,long long>>> g;
	stack<long long> s;
	vector<long long> c;
	vector<vector<long long>> r;
	SCC(vector<vector<long long>> g) : n(g.size()) {
		this->g.resize(g.size(),{});
		for(long long i = 0ll;i < n;i++){
			if(g[i].empty()) continue;
			for(long long j : g[i]){
				this->g[i].push_back({j,true});
				this->g[j].push_back({i,false});
			}
		}
	}
	vector<vector<long long>> get(){
		c.resize(n,false);
		for(long long i = 0ll;i < n;i++){
			if(!c[i]){
				dfs(i);
			}
		}
		for(long long i = 0ll;i < n;i++){
			c[i] = false;
		}
		r = {};
		while(!s.empty()){
			long long it = s.top();
			s.pop();
			if(!c[it]){
				r.push_back({});
				dfs2(it);
			}
		}
		return r;
	}
	void dfs(long long x){
		c[x] = true;
		if(!g[x].empty()){
			for(pair<long long,long long> i : g[x]){
				if(!i.second) continue;
				if(c[i.first]) continue;
				dfs(i.first);
			}
		}
		s.push(x);
	}
	void dfs2(long long x){
		c[x] = true;
		r.back().push_back(x);
		if(g[x].empty()) return;
		for(pair<long long,long long> i : g[x]){
			if(i.second) continue;
			if(c[i.first]) continue;
			dfs2(i.first);
		}
	}
};
int main()
{
	long long n,m;
	cin >> n >> m;
	vector<vector<long long>> g(n,vector<long long>());
	for(long long i = 0ll;i < m;i++){
		long long u,v;
		cin >> u >> v;
		g[u].push_back(v);
	}
	SCC sg(g);
	vector<vector<long long>> it = sg.get();
	vector<long long> t(n);
	vector<pair<long long,long long>> cou(it.size(),pair<long long,long long>(0ll,0ll));
	if((long long)it.size() == 1ll){
		cout << 0ll << endl;
		exit(0ll);
	}
	for(long long i = 0ll;i < (long long)it.size();i++){
		for(long long j : it[i]){
			t[j] = i;
		}
	}
	for(long long i = 0ll;i < n;i++){
		if(!g[i].empty()){
			for(long long j : g[i]){
				if(t[i] != t[j]){
					cou[t[i]].first++;
					cou[t[j]].second++;
				}
			}
		}
	}
	pair<long long,long long> p(0ll,0ll);
	for(long long i = 0ll;i < (long long)cou.size();i++){
		if(cou[i].first == 0ll){
			p.first++;
		}
		if(cou[i].second == 0ll){
			p.second++;
		}
	}
	long long ans = max(p.first,p.second);
	cout << ans << endl;
}
accuracy: 1
time_ms: 30
memory_kb: 11572
final_rank: 13

code_snippet: #include <bits/stdc++.h>

#include <algorithm>

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

// Reference:
// R. Tarjan,
// Depth-First Search and Linear Graph Algorithms
struct scc_graph {
  public:
    scc_graph(int n) : _n(n) {}

    int num_vertices() { return _n; }

    void add_edge(int from, int to) { edges.push_back({from, {to}}); }

    // @return pair of (# of scc, scc id)
    std::pair<int, std::vector<int>> scc_ids() {
        auto g = csr<edge>(_n, edges);
        int now_ord = 0, group_num = 0;
        std::vector<int> visited, low(_n), ord(_n, -1), ids(_n);
        visited.reserve(_n);
        auto dfs = [&](auto self, int v) -> void {
            low[v] = ord[v] = now_ord++;
            visited.push_back(v);
            for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                auto to = g.elist[i].to;
                if (ord[to] == -1) {
                    self(self, to);
                    low[v] = std::min(low[v], low[to]);
                } else {
                    low[v] = std::min(low[v], ord[to]);
                }
            }
            if (low[v] == ord[v]) {
                while (true) {
                    int u = visited.back();
                    visited.pop_back();
                    ord[u] = _n;
                    ids[u] = group_num;
                    if (u == v) break;
                }
                group_num++;
            }
        };
        for (int i = 0; i < _n; i++) {
            if (ord[i] == -1) dfs(dfs, i);
        }
        for (auto& x : ids) {
            x = group_num - 1 - x;
        }
        return {group_num, ids};
    }

    std::vector<std::vector<int>> scc() {
        auto ids = scc_ids();
        int group_num = ids.first;
        std::vector<int> counts(group_num);
        for (auto x : ids.second) counts[x]++;
        std::vector<std::vector<int>> groups(ids.first);
        for (int i = 0; i < group_num; i++) {
            groups[i].reserve(counts[i]);
        }
        for (int i = 0; i < _n; i++) {
            groups[ids.second[i]].push_back(i);
        }
        return groups;
    }

  private:
    int _n;
    struct edge {
        int to;
    };
    std::vector<std::pair<int, edge>> edges;
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <vector>

namespace atcoder {

struct scc_graph {
  public:
    scc_graph() : internal(0) {}
    scc_graph(int n) : internal(n) {}

    void add_edge(int from, int to) {
        int n = internal.num_vertices();
        assert(0 <= from && from < n);
        assert(0 <= to && to < n);
        internal.add_edge(from, to);
    }

    std::vector<std::vector<int>> scc() { return internal.scc(); }

  private:
    internal::scc_graph internal;
};

}  // namespace atcoder

using namespace std;
using namespace atcoder;

int main()
{
	int n,m;
	cin >> n >> m;
	scc_graph sg(n);
	vector<vector<int>> g(n,vector<int>());
	for(int i = 0;i < m;i++){
		int u,v;
		cin >> u >> v;
		sg.add_edge(u,v);
		g[u].push_back(v);
	}
	vector<vector<int>> it = sg.scc();
	vector<int> t(n);
	vector<pair<int,int>> cou(it.size(),pair<int,int>(0,0));
	if((int)it.size() == 1){
		cout << 0 << endl;
		exit(0);
	}
	for(int i = 0;i < (int)it.size();i++){
		for(int j : it[i]){
			t[j] = i;
		}
	}
	for(int i = 0;i < n;i++){
		if(!g[i].empty()){
			for(int j : g[i]){
				if(t[i] != t[j]){
					cou[t[i]].first++;
					cou[t[j]].second++;
				}
			}
		}
	}
	pair<int,int> p(0,0);
	for(int i = 0;i < (int)cou.size();i++){
		if(cou[i].first == 0){
			p.first++;
		}
		if(cou[i].second == 0){
			p.second++;
		}
	}
	int ans = max(p.first,p.second);
	cout << ans << endl;
}
accuracy: 1
time_ms: 30
memory_kb: 6896
final_rank: 8

code_snippet: ﻿// ### test.cpp ###
#include <bits/stdc++.h>
#ifdef __DEBUG_VECTOR
namespace for_debugging{
    struct subscript_and_location{
        int sub;
        std::source_location loc;
        subscript_and_location(int sub_,std::source_location loc_=std::source_location::current()){
            sub=sub_;
            loc=loc_;
        }
        void check_out_of_range(size_t sz){
            if(sub<0||(int)sz<=sub){
                std::clog << loc.file_name() << ":(" << loc.line() << ":" << loc.column() << "):" << loc.function_name() << std::endl;
                std::clog << "out of range: subscript = " << sub << ", vector_size = " << sz << std::endl;
                exit(EXIT_FAILURE);
            }
        }
    };
}
namespace std{
    template<class T,class Allocator=std::allocator<T>> class vector_for_debugging:public std::vector<T,Allocator>{
        using std::vector<T,Allocator>::vector;
        public:
            [[nodiscard]] constexpr std::vector<T,Allocator>::reference operator[](for_debugging::subscript_and_location n) noexcept(!std::is_same<T,bool>::value){
                n.check_out_of_range(this->size());
                return std::vector<T,Allocator>::operator[](n.sub);
            }
            [[nodiscard]] constexpr std::vector<T,Allocator>::const_reference operator[](for_debugging::subscript_and_location n) const noexcept(!std::is_same<T,bool>::value){
                n.check_out_of_range(this->size());
                return std::vector<T,Allocator>::operator[](n.sub);
            }
    };
    namespace pmr{
        template<class T> using vector_for_debugging=std::vector_for_debugging<T,std::pmr::polymorphic_allocator<T>>;
    }
}
#define vector vector_for_debugging
#endif
using namespace std;
using ll = long long;
using ull = unsigned long long;
using db = double;
using Pr = pair<ll, ll>;
using Pd = pair<double, double>;
using vi = vector<int>;
using vs = vector<string>;
using vc = vector<char>;
using vl = vector<ll>;
using vb = vector<bool>;
using vd = vector<double>;
using vp = vector<Pr>;
using vpd = vector<Pd>;
using vvi = vector<vector<int>>;
using vvc = vector<vector<char>>;
using vvl = vector<vector<ll>>;
using vvp = vector<vector<Pr>>;
using vvb = vector<vector<bool>>;
using vvd = vector<vector<double>>;
using vvs = vector<vector<string>>;
using vvvi = vector<vector<vector<int>>>;
using vvvl = vector<vector<vector<ll>>>;
using vvvb = vector<vector<vector<bool>>>;
using vvvd = vector<vector<vector<double>>>;
using t3 = tuple<ll,ll,ll>;
using t4 = tuple<ll,ll,ll,ll>;
using vt3 = vector<t3>;
using vt4 = vector<t4>;
using vvt3 = vector<vector<t3>>;
using vvt4 = vector<vector<t4>>;
using pq = priority_queue<Pr,vector<Pr>,greater<Pr>>;
using cl = complex<ll>;
using cd = complex<double>;
#define rep(i, N) for (ll i=0; i<(ll)(N); i++)
#define repr(i, N) for (ll i = (ll)(N) - 1; i >= 0; i--)
#define repk(i, k, N) for (ll i = k; i < (ll)(N); i++)
#define rep1(i, N) for (ll i=1; i<(ll)(N+1); i++)
#define all(v) (v).begin(), (v).end()
#define allr(v) (v).rbegin(), (v).rend()
#define SIZE(v) (ll)((v).size())
#define PYes {puts("Yes"); exit(0);}
#define PNo {puts("No"); exit(0);}
#define Pm0 {puts("0"); exit(0);}
#define Pm1 {puts("-1"); exit(0);}
#define INT(...) int __VA_ARGS__; in(__VA_ARGS__)
#define INTM(...) int __VA_ARGS__; inm(__VA_ARGS__)
#define LONG(...) ll __VA_ARGS__; in(__VA_ARGS__)
#define LONGM(...) ll __VA_ARGS__; inm(__VA_ARGS__)
#define DOUBLE(...) double __VA_ARGS__; in(__VA_ARGS__)
#define CHAR(...) char __VA_ARGS__; in(__VA_ARGS__)
#define STRING(...) string __VA_ARGS__; in(__VA_ARGS__)
#define VI(ivec, n) vi ivec; input_ivec(ivec, n)
#define VIM(ivec, n) vi ivec; input_ivecm(ivec, n)
#define VL(lvec, n) vl lvec; input_lvec(lvec, n)
#define VLM(lvec, n) vl lvec; input_lvecm(lvec, n)
#define VC(cvec, n) vc cvec; input_cvec(cvec, n)
#define VS(svec, n) vs svec; input_svec(svec, n)
#define VD(dvec, n) vd dvec; input_dvec(dvec, n)
#define VP(pvec, n) vp pvec; input_pvec(pvec, n)
#define VPD(pvec, n) vpd pvec; input_pvecd(pvec, n)
#define VPM(pvec, n) vp pvec; input_pvecm(pvec, n)
#define VVI(ivec2, h, w) vvi ivec2(h, vi(w)); input_ivec2(ivec2, h, w)
#define VVL(lvec2, h, w) vvl lvec2(h, vl(w)); input_lvec2(lvec2, h, w)
#define VVLM(lvec2, h, w) vvl lvec2(h, vl(w)); input_lvec2m(lvec2, h, w)
#define VVC(cvec2, h, w) vvc cvec2(h, vc(w)); input_cvec2(cvec2, h, w)
#define pcnt(x) (ll)__builtin_popcountll(x)
#define parity(x) (ll)__builtin_parityll(x)
#define abs(x) llabs(x)
#define uset unordered_set
#define umap unordered_map
inline void Out(double x) {printf("%.15f",x);cout<<'\n';}
template<typename T> inline void Out(pair<T,T> x) {cout<<x.first<<' '<<x.second<<'\n';}
template<typename T> inline void Out(T x) {cout<<x<<'\n';}
inline void Out(vector<string> v) {rep(i,SIZE(v)) cout<<v[i]<<'\n';}
template<typename T> inline void Out(queue<T> q){while(!q.empty()) {cout<<q.front()<<" "; q.pop();} cout<<endl;}
template<typename T> inline void Out(deque<T> q){while(!q.empty()) {cout<<q.front()<<" "; q.pop_front();} cout<<endl;}
template<typename T> inline void Out(vector<T> v) {rep(i,SIZE(v)) cout<<v[i]<<(i==SIZE(v)-1?'\n':' ');}
template<typename T> inline void Out(vector<vector<T>> &vv){for(auto &v: vv) Out(v);}
template<typename T> inline void Out(vector<pair<T,T>> v) {for(auto p:v) Out(p);}
template<typename T> inline void Outend(T x) {Out(x); exit(0);}
template<typename T> inline void chmin(T &a, T b) { a = min(a, b); }
template<typename T> inline void chmax(T &a, T b) { a = max(a, b); }
inline void mi(void) {return;}
template<typename T1, typename... T2> void mi(T1& f, T2&... r) {--f; mi(r...);}
template<class... T> void in(T&... x) {(cin >> ... >> x);}
template<class... T> void inm(T&... x) {(cin >> ... >> x); mi(x...);}
inline void input_ivec(vi &ivec, int n) {rep(i, n) {int x; cin >> x; ivec.push_back(x);}}
inline void input_ivecm(vi &ivec, int n) {rep(i, n) {int x; cin >> x; ivec.push_back(--x);}}
inline void input_lvec(vl &lvec, ll n) {rep(i, n) {ll x; cin >> x; lvec.push_back(x);}}
inline void input_lvecm(vl &lvec, ll n) {rep(i, n) {ll x; cin >> x; lvec.push_back(--x);}}
inline void input_cvec(vc &cvec, ll n) {rep (i, n) {char c; cin >> c; cvec.push_back(c);}}
inline void input_svec(vs &svec, ll n) {rep (i, n) {string s; cin >> s; svec.push_back(s);}}
inline void input_dvec(vd &dvec, ll n) {rep (i, n) {double d; cin >> d; dvec.push_back(d);}}
inline void input_pvec(vp &pvec, ll n) {rep (i, n) {ll a, b; cin >> a >> b; pvec.emplace_back(a, b);}}
inline void input_pvecm(vp &pvec, ll n) {rep (i, n) {ll a, b; cin >> a >> b; pvec.emplace_back(--a, --b);}}
inline void input_pvecd(vpd &pvec, ll n) {rep (i, n) {double a, b; cin >> a >> b; pvec.emplace_back(a, b);}}
inline void input_ivec2(vvi &ivec2, int h, int w) {rep(i, h) rep(j, w) {int x; cin >> x; ivec2[i][j] = x;}}
inline void input_lvec2(vvl &lvec2, ll h, ll w) {rep(i, h) rep(j, w) {ll x; cin >> x; lvec2[i][j] = x;}}
inline void input_lvec2m(vvl &lvec2, ll h, ll w) {rep(i, h) rep(j, w) {ll x; cin >> x; lvec2[i][j] = --x;}}
inline void input_cvec2(vvc &cvec2, ll h, ll w) {rep(i, h) rep(j, w) {char c; cin >> c; cvec2[i][j] = c;}}
inline bool isin(ll i, ll j, ll h, ll w) {if(i<0||i>=h||j<0||j>=w) return false; else return true;}
inline ll TmpPercent(ll a, ll b) {if(b<0){a=-a,b=-b;} return (a%b+b)%b;}
inline ll Percent(ll a, ll b) {if(b<0) return -TmpPercent(a,b); return TmpPercent(a,b);}
inline ll Div(ll a, ll b) {if(b<0){a=-a,b=-b;} return (a-TmpPercent(a,b))/b; }
inline ll Divceil(ll a, ll b) {if(TmpPercent(a,b)==0) return Div(a,b); return Div(a,b)+1;}
#ifdef __DEBUG
#define de(var) {cerr << #var << ": "; debug_view(var);}
#define de2(var1,var2) {cerr<<#var1<<' '<<#var2<<": "; debug_view(var1,var2);}
#define de3(var1,var2,var3) {cerr<<#var1<<' '<<#var2<<' '<<#var3<<": "; debug_view(var1,var2,var3);}
#define de4(var1,var2,var3,var4) {cerr<<#var1<<' '<<#var2<<' '<<#var3<<' '<<#var4<<": "; debug_view(var1,var2,var3,var4);}
template<typename T> inline void debug_view(T e){cerr << e << endl;}
template<typename T1, typename T2> inline void debug_view(T1 e1, T2 e2){cerr<<e1<<' '<<e2<<endl;}
template<typename T1, typename T2, typename T3> inline void debug_view(T1 e1, T2 e2, T3 e3){cerr<<e1<<' '<<e2<<' '<<e3<<endl;}
template<typename T1, typename T2, typename T3, typename T4> inline void debug_view(T1 e1, T2 e2, T3 e3, T4 e4){cerr<<e1<<' '<<e2<<' '<<e3<<' '<<e4<<endl;}
template<typename T1, typename T2> inline void debug_view(pair<T1,T2> &p){cerr<<"{"<<p.first<<" "<<p.second<<"}\n";}
template<typename T1, typename T2> inline void debug_view(vector<pair<T1,T2>> &v){for(auto [a,b]: v){cerr<<"{"<<a<<" "<<b<<"} ";} cerr << endl;}
template<typename T1, typename T2> inline void debug_view(set<pair<T1,T2>> &s){for(auto [a,b]: s){cerr<<"{"<<a<<" "<<b<<"} ";} cerr << endl;}
template<typename T> inline void debug_view(tuple<T,T,T> t){cerr<<get<0>(t)<<' '<<get<1>(t)<<' '<<get<2>(t)<< endl;}
template<typename T> inline void debug_view(queue<T> q){while(!q.empty()) {cerr << q.front() << " "; q.pop();}cerr << endl;}
template<typename T> inline void debug_view(deque<T> q){while(!q.empty()) {cerr << q.front() << " "; q.pop_front();}cerr << endl;}
template<typename T> inline void debug_view(set<T> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(set<T,greater<T>> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(unordered_set<T> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(multiset<T> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(multiset<T,greater<T>> s){for(auto x:s){cerr << x << ' ';}cerr << endl;}
template<typename T> inline void debug_view(vector<pair<T,T>> &v){for(auto [a,b]: v){cerr<<"{"<<a<<" "<<b<<"} ";} cerr << endl;}
inline void debug_view(vector<string> &v){cerr << "----" << endl; for(auto s: v) debug_view(s);}
template<typename T> inline void debug_view(vector<T> &v){for(auto e: v){cerr << e << " ";} cerr << endl;}
template<typename T> inline void debug_view(vector<vector<pair<T,T>>> &vv){cerr << "----" << endl;for(auto &v: vv){debug_view(v);} cerr << "--------" << endl;}
template<typename T> inline void debug_view(vector<vector<T>> &vv){cerr << "----" << endl;for(auto &v: vv){debug_view(v);} cerr << "--------" << endl;}
template<typename T1,typename T2> inline void debug_view(map<T1,T2> &mp){cerr << "----" << endl;for(auto [k,v]: mp){cerr << k << ' ' << v << endl;} cerr << "--------" << endl;}
template<typename T1,typename T2> inline void debug_view(unordered_map<T1,T2> &mp){cerr << "----" << endl;for(auto [k,v]: mp){cerr << k << ' ' << v << endl;} cerr << "--------" << endl;}
template<typename T1,typename T2> inline void debug_view(map<T1,vector<T2>> &mp){cerr<<"----"<<endl;for(auto [k,v]: mp){cerr<<k<<": ";debug_view(v);} cerr << "--------" << endl;}
template<typename T1,typename T2,typename T3> inline void debug_view(map<pair<T1,T2>,T3> &mp){cerr << "----" << endl;for(auto [p,v]: mp){cerr<<'{'<<p.first<<' '<<p.second<<'}'<<": "<<v<<endl;} cerr<<"--------"<<endl;}
#define deb(var) {cerr << #var << ": "; debugb_view(var);}
template<typename T> inline void debugb_view(T e){bitset<20> b(e); cerr<<b<<endl;}
template<typename T> inline void debugb_view(vector<T> &v){cerr<<"----"<<endl;for(auto e: v){debugb_view(e);}}
#else
#define de(var) {}
#define de2(var1,var2) {}
#define de3(var1,var2,var3) {}
#define de4(var1,var2,var3,var4) {}
#define deb(var) {}
#endif
ll INF = 3e18;
const ll M998 = 998244353;
const ll M107 = 1000000007;
template<typename T> inline void ch1(T &x){if(x==INF)x=-1;}
const double PI = acos(-1);
const double EPS = 1e-8;  //eg) if x=1e9, EPS >= 1e9/1e15(=1e-6)
const vi di = {0, 1, 0, -1};
const vi dj = {1, 0, -1, 0};
const vp dij = {{0,1},{1,0},{0,-1},{-1,0}};
const vi di8 = {-1, -1, -1, 0, 0, 1, 1, 1};
const vi dj8 = {-1, 0, 1, -1, 1, -1, 0, 1};
Pr operator+ (Pr a, Pr b) {return {a.first+b.first, a.second+b.second};}
Pr operator- (Pr a, Pr b) {return {a.first-b.first, a.second-b.second};}
Pr operator* (Pr a, Pr b) {return {a.first*b.first, a.second*b.second};}
Pr operator/ (Pr a, Pr b) {return {a.first/b.first, a.second/b.second};}

struct SCC {
    SCC (long long _n): n(_n), from(_n), ifrom(_n) {}
    void add_edge (long long a, long long b) {
        from[a].push_back(b);
        ifrom[b].push_back(a);
    }
    vector<vector<long long>> scc () {
        vector<vector<long long>> group;
        back_num.clear();
        selected.assign(n, false);
        for (long long i=0; i < n; ++i) {
            if (!selected[i]) dfs1(i);
        }
        selected.assign(n, false);
        for (long long i=n-1; i >= 0; --i) {
            long long x = back_num[i];
            if (selected[x]) continue;
            vector<long long> emp;
            dfs2(x, emp);
            group.push_back(emp);
        }
        return group;
    }
private:
    long long n;
    vector<vector<long long>> from, ifrom;
    vector<long long> back_num;
    vector<bool> selected;
    void dfs1 (long long x) {
        selected[x] = true;
        for (auto y: from[x]) {
            if (selected[y]) continue;
            dfs1(y);
        }
        back_num.push_back(x);
    }
    void dfs2 (long long x, vector<long long> &vec) {
        selected[x] = true;
        vec.push_back(x);
        for (auto y: ifrom[x]) {
            if (selected[y]) continue;
            dfs2(y, vec);
        }
    }
};



int main () {
    // ios::sync_with_stdio(false);
    cin.tie(nullptr);
    LONG(N, M);
    SCC scc(N);
    vp edge;
    rep(i, M) {
        LONG(s, t);
        scc.add_edge(s, t);
        edge.emplace_back(s, t);
    }
    auto grs = scc.scc();
    vl group(N);
    ll idx = 0;
    for(auto gr: grs) {
        for(auto v: gr) {
            group[v] = idx;
        }
        ++idx;
    }
    if(SIZE(grs)==1) Pm0
    de(grs)
    ll Z = SIZE(grs);
    vl ind(Z), outd(Z);
    for(auto [s,t]: edge) {
        if(group[s]==group[t]) continue;
        outd[group[s]]++;
        ind[group[t]]++;
    }
    ll nin=0, nout=0;
    de(ind)de(outd)
    rep(i, Z) {
        if(ind[i]==0) nin++;
        if(outd[i]==0) nout++;
    }
    Out(max(nin, nout));
    
}

// ### test.cpp ###
accuracy: 1
time_ms: 30
memory_kb: 9120
final_rank: 10

code_snippet: #include <bits/stdc++.h>
using namespace std;

int N = 11000, c = 0;

//辺を保存(way2は逆向きで)
vector < vector < int > > way(N) ,way2(N);

//dfsにおいて判定したかどうか
vector < bool > checked(N ,0);

//dfsにより並び替えた後の配列
vector < int > LIST(N, -1);

//頂点が属する連結成分の番号
vector < int > ans(N, -1);

//番号の割り付け
void dfs(int pos){
    checked[pos] = 1;
    for(auto i : way[pos]){
        if(checked[i] == 0){
            dfs(i);
        }
    }
    LIST[c] = pos;
    c++;
}

//頂点が属する連結成分の判定
void dfs2(int pos,int num){
    ans[pos] = num;
    for(auto i : way2[pos]){
        if(ans[i] == -1){
            dfs2(i, num);
        }
    }
}

int main(){
    int v, e;
    cin >> v >> e ;
    for(int i = 0;i < e;i++){
        int s, t;
        cin >> s >> t ;
        // s--,t--;
        way[s].push_back(t);
        way2[t].push_back(s);
    }

    for(int i = 0;i < v;i++){
        if(checked[i] == 0){
            dfs(i);
        }
    }

    for(int i = v-1;i >= 0&&LIST[i] != -1;i--){
        if(ans[ LIST[i] ] == -1){
            dfs2(LIST[i],LIST[i]);
        }
    }

    //新しいグラフに直す
    //座圧 -> グラフの作成(辺の本数を記録、逆方向の辺も張る)
    vector<int>zaatu(v);
    for(int i = 0;i < v;i++)zaatu[i] = ans[i];
    sort(zaatu.begin(),zaatu.end());
    zaatu.erase(unique(zaatu.begin(),zaatu.end()),zaatu.end());
    if(zaatu.size() == 1){
        cout <<"0\n";
        return 0;
    }

    for(int i = 0;i < v;i++){
        ans[i] = lower_bound(zaatu.begin(),zaatu.end(),ans[i])-zaatu.begin();
    }
    
    vector<vector<int>>way3(zaatu.size()),way4(zaatu.size());
    for(int i = 0;i < v;i++){
        for(auto j:way[i]){
            if(ans[i] == ans[j])continue;
            way3[ans[i]].push_back(ans[j]);
        }
        for(auto j:way2[i]){
            if(ans[i] == ans[j])continue;
            way4[ans[i]].push_back(ans[j]);
        }
    }

    int in = 0,out = 0;
    for(auto i:way3)if(i.empty())in++;
    for(auto i:way4)if(i.empty())out++;

    cout <<max(in,out)<<"\n";
}
accuracy: 1
time_ms: 30
memory_kb: 6896
final_rank: 8