// ライブラリ
#include<unordered_map>
#include<unordered_set>
#include<stdexcept>
#include<queue>
#include<stack>
#include<tuple>
#include<functional>
#include<algorithm>
// クラス
template<typename Label, typename Weight>
class Graph {
public:
  const Weight ZERO;
public:
  explicit Graph(const Weight& z = Weight());   // !!!! double とか使ったときの Weight() の初期値はコンパイラ依存 多分 !!!!
  std::unordered_map<Label, Weight>& operator[](const Label& i);
  bool exist(const Label& from, const Label& to) const;
  std::unordered_map<Label, Weight> Dijkstra(const Label& from) const;
  Weight EdmondsKarp(const Label& from, const Label& to) const;
  std::vector<std::vector<Label>> Kosaraju() const;
private:
  void Kosaraju_postorder(const Label& from, std::unordered_set<Label>& visited, std::stack<Label>& order) const;
  const std::vector<Label> Kosaraju_decompose(const std::unordered_map<Label, std::unordered_map<Label, Weight>>& reverse, const Label& from, std::unordered_set<Label>& visited) const;
private:
  std::unordered_map<Label, std::unordered_map<Label, Weight>> adjacency_;
};
// コンストラクタ
template<typename Label, typename Weight>
Graph<Label, Weight>::Graph(const Weight& z) : ZERO(z) {}
// アクセサ
template<typename Label, typename Weight>
std::unordered_map<Label, Weight>& Graph<Label, Weight>::operator[](const Label& i) {
  return adjacency_[i];
}
// 辺の存在判定
template<typename Label, typename Weight>
bool Graph<Label, Weight>::exist(const Label& from, const Label& to) const {
  try {adjacency_.at(from).at(to);}
  catch(const std::out_of_range& oor) {return false;}
  return true;
}
// 最短経路 O(|E| + |V| * log(|V|)) ※ C++ の priority_queue は ParingHeap ≒ フィボナッチヒープ
template<typename Label, typename Weight>
std::unordered_map<Label, Weight> Graph<Label, Weight>::Dijkstra(const Label& from) const {
  typedef std::tuple<Weight, Label> Node;
  std::unordered_map<Label, Weight> distance;
  std::priority_queue<Node, std::vector<Node>, std::greater<Node>> q;
  q.push(Node(ZERO, from));
  while(!q.empty()) {
    Weight cost;
    Label current;
    std::tie(cost, current) = q.top();
    q.pop();
    if(distance.count(current)) continue;
    distance[current] = cost;
    if(adjacency_.count(current)) for(const auto& e: adjacency_.at(current))
      q.push(Node(cost + e.second, e.first));
  }
  return distance;
}
// 最大流・最小カット O(|V| * |E|^2)
template<typename Label, typename Weight>
Weight Graph<Label, Weight>::EdmondsKarp(const Label& from, const Label& to) const {
  Weight result = ZERO;
  std::unordered_map<Label, std::unordered_map<Label, Weight>> flow, capacity(adjacency_);
  for(const auto& u: capacity) for(const auto& v: u.second) flow[u.first][v.first] = ZERO;
  while(true) {
    // 増加道を探す
    std::unordered_map<Label, Label> previous;
    std::queue<Label> q;
    q.push(from);
    while(!q.empty()) {
      Label current = q.front();
      q.pop();
      if(current == to) break;
      if(adjacency_.count(current)) for(const auto& e: adjacency_.at(current)) {
        if(e.first == from) continue;
        if(previous.count(e.first)) continue;
        if(capacity[current][e.first] - flow[current][e.first] <= ZERO) continue;
        previous[e.first] = current;
        q.push(e.first);
      }
    }
    if(!previous.count(to)) break;
    // 流量を決定
    Weight add = capacity[previous[to]][to] - flow[previous[to]][to];
    for(Label v = to; previous.count(v); v = previous[v]) add = std::min(add, capacity[previous[v]][v] - flow[previous[v]][v]);
    // 流す
    for(Label v = to; previous.count(v); v = previous[v]) {
      flow[previous[v]][v] += add;
      flow[v][previous[v]] -= add;
    }
    result += add;
  }
  return result;
}
// 強連結成分分解 O(|V| + |E|)
template<typename Label, typename Weight>
std::vector<std::vector<Label>> Graph<Label, Weight>::Kosaraju() const {
  std::vector<std::vector<Label>> result;
  std::unordered_set<Label> visited;
  std::stack<Label> order;
  for(const auto& list: adjacency_) if(!visited.count(list.first)) Kosaraju_postorder(list.first, visited, order);
  std::unordered_map<Label, std::unordered_map<Label, Weight>> reverse_edge;
  for(const auto& list: adjacency_) for(const auto& e: list.second) reverse_edge[e.first][list.first] = e.second;
  visited.clear();
  while(!order.empty()) {
    Label v = order.top();
    order.pop();
    if(!visited.count(v)) result.push_back(Kosaraju_decompose(reverse_edge, v, visited));
  }
  return result;
}
template<typename Label, typename Weight>
void Graph<Label, Weight>::Kosaraju_postorder(const Label& from, std::unordered_set<Label>& visited, std::stack<Label>& order) const {
  visited.insert(from);
  if(adjacency_.count(from)) for(const auto& e: adjacency_.at(from))
    if(!visited.count(e.first)) Kosaraju_postorder(e.first, visited, order);
  order.push(from);
}
template<typename Label, typename Weight>
const std::vector<Label> Graph<Label, Weight>::Kosaraju_decompose(const std::unordered_map<Label, std::unordered_map<Label, Weight>>& reverse_edge, const Label& from, std::unordered_set<Label>& visited) const {
  std::vector<Label> result({from});
  visited.insert(from);
  if(reverse_edge.count(from)) for(const auto& e: reverse_edge.at(from)) {
    if(visited.count(e.first)) continue;
    auto add = Kosaraju_decompose(reverse_edge, e.first, visited);
    result.insert(end(result), begin(add), end(add));
  }
  return result;
}

////////////////////////////////////////////////////////////////////////////////
#include<bits/stdc++.h>
using namespace std;
int main() {
  int V, E;
  cin >> V >> E;
  Graph<int, int> g;
  unordered_set<int> vertex;
  for(int i = 0; i < E; ++i) {
    int s, t;
    cin >> s >> t;
    g[s][t] = 1;
    vertex.insert(s);
    vertex.insert(t);
  }
  auto scc = g.Kosaraju();
  cout << (scc.size() == vertex.size() ? 0 : 1) << endl;
}

Measured memory_bytes: 1372