// グラフ

// 隣接リスト

#include<iostream>
#include<vector>
#include<queue>
#include<functional>
#include<cfloat>
#include<tuple>
#include<set>
#include<stack>

const double INF = DBL_MAX / 3.0;

// Edge
class Edge {
public:
    Edge() {}
    Edge(int f, int t, double w) :from_(f), to_(t), weight_(w) {}
    int from() const {return from_;}
    int to() const {return to_;}
    double weight() const {return weight_;}
private:
    int from_, to_;
    double weight_;
};

// Graph
class Graph {
public:
    explicit Graph(const int s) :size_(s) {edge_.resize(s);}

    int size() const {return size_;}
    const std::vector<Edge>& edge(const int v) const {return edge_[v];}

    void add(const Edge& e);
    void add(const int from, const int to, const double weight = 1.0);
    double Dijkstra(const int start, const int goal) const;
    double BellmanFord(const int start, const int goal) const;
    bool cut_vertex(const int v, const std::set<int>& visited) const;
    std::vector<std::vector<int>> Kosaraju() const;

private:
    void cut_vertex_reachability(const int from, std::set<int>& visited) const;
    void Kosaraju_dfs_postorder(const int from, std::vector<bool>& visited, std::stack<int>& order) const;
    std::vector<int> Kosaraju_dfs_decompose(const std::vector<std::vector<Edge>>& reverse_edge, const int from, std::vector<bool>& visited) const;

    int size_;
    std::vector<std::vector<Edge>> edge_;
};

// implements
void Graph::add(const Edge& e) {edge_[e.from()].push_back(e);}

void Graph::add(const int from, const int to, const double weight) {edge_[from].push_back(Edge(from, to, weight));}

double Graph::Dijkstra(const int start, const int goal) const {
    enum {COST, VERTEX};
    typedef std::tuple<double, int> Node;

    std::vector<int> visited(size(), 0);
    std::priority_queue<Node, std::vector<Node>, std::greater<Node>> q;
    q.push(Node(0, start));
    while(!q.empty()) {
        double cost = std::get<COST>(q.top());
        int from = std::get<VERTEX>(q.top());
        q.pop();
        if(from == goal) return cost;
        if(visited[from]) continue;
        visited[from] = 1;
        for(const auto& e: edge(from)) q.push(Node(cost + e.weight(), e.to()));
    }
    return INF;
}

double Graph::BellmanFord(const int start, const int goal) const {
    std::vector<double> distance(size(), INF);
    distance[start] = 0;
    for(int step = 0; step <= size(); ++step) {
        for(int from = 0; from < size(); ++from) {
            for(const auto& e: edge(from)) {
                if(distance[from] + e.weight() >= distance[e.to()]) continue;
                if(step == size()) return INF;
                distance[e.to()] = distance[from] + e.weight();
            }
        }
    }
    return distance[goal];
}

bool Graph::cut_vertex(const int v, const std::set<int>& visited = std::set<int>()) const {
    std::set<int> dup = visited;
    for(const auto& e: edge(v)) {
        if(dup.count(e.to())) continue;
        cut_vertex_reachability(e.to(), dup);
        return dup.size() != size();
    }
    return dup.size() != size();
}
void Graph::cut_vertex_reachability(const int from, std::set<int>& visited) const {
    visited.insert(from);
    for(const auto& e: edge(from)) if(!visited.count(e.to())) cut_vertex_reachability(e.to(), visited);
}

std::vector<std::vector<int>> Graph::Kosaraju() const {
    std::vector<std::vector<int>> result;

    std::vector<bool> visited(size(), false);
    std::stack<int> order;
    for(int v = 0; v < size(); ++v) if(!visited[v]) Kosaraju_dfs_postorder(v, visited, order);

    std::vector<std::vector<Edge>> reverse_edge(size(), std::vector<Edge>());
    for(int v = 0; v < size(); ++v) for(const auto& e: edge(v)) reverse_edge[e.to()].push_back(Edge(e.to(), e.from(), e.weight()));

    visited = std::vector<bool>(size(), false);
    while(!order.empty()) {
        int v = order.top();
        order.pop();
        if(visited[v]) continue;
        result.push_back(Kosaraju_dfs_decompose(reverse_edge, v, visited));
    }
    return result;
}
void Graph::Kosaraju_dfs_postorder(const int from, std::vector<bool>& visited, std::stack<int>& order) const {
    visited[from] = true;
    for(const auto& e: edge(from)) if(!visited[e.to()]) Kosaraju_dfs_postorder(e.to(), visited, order);
    order.push(from);
}
std::vector<int> Graph::Kosaraju_dfs_decompose(const std::vector<std::vector<Edge>>& reverse_edge, const int from, std::vector<bool>& visited) const {
    std::vector<int> result;
    result.push_back(from);
    visited[from] = true;
    for(const auto& e: reverse_edge[from]) {
        if(visited[e.to()]) continue;
        std::vector<int> add = Kosaraju_dfs_decompose(reverse_edge, e.to(), visited);
        result.insert(result.end(), add.begin(), add.end());
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////

using namespace std;

int main() {
    int V, E;
    cin >> V >> E;
    Graph g(V);
    for(int i = 0; i < E; ++i) {
        int s, t;
        cin >> s >> t;
        g.add(s, t);
    }
    auto scc = g.Kosaraju();
    cout << (scc.size() == V ? 0 : 1) << endl;
}

Measured memory_bytes: 1292