// #include "longest_path.h"
#include <algorithm>
#include <iostream>
#include <queue>
#include <stdexcept>

class Graph {
public:
    Graph(size_t n);

    void add_edge(size_t from, size_t to);

    size_t num_nodes() const;

    const std::vector<size_t>& neighbors(size_t n) const;

private:
    size_t n;
    std::vector<std::vector<size_t>> data =
        std::vector<std::vector<size_t>>(n + 1, std::vector<size_t>{});
};

namespace {

Graph reverse(const Graph& graph);

std::vector<size_t> compute_in_degree(const Graph& graph);

std::queue<size_t>
all_nodes_with_zero_in_degree(const std::vector<size_t>& in_degree);

std::vector<size_t> topological_sort(const Graph& graph);

} // namespace

Graph::Graph(size_t n)
    : n{n}
{
}

void Graph::add_edge(size_t from, size_t to) { data[from].push_back(to); }

size_t Graph::num_nodes() const { return n; }

const std::vector<size_t>& Graph::neighbors(size_t node) const
{
    return data[node];
}

size_t length_of_longest_path(const Graph& graph)
{
    std::vector<size_t> path_lengths(graph.num_nodes() + 1);
    const auto topological_order = topological_sort(graph);
    Graph reversed_graph = reverse(graph);

    for (auto node : topological_order) {
        size_t max{0};
        for (size_t neighbor : reversed_graph.neighbors(node)) {
            max = std::max(max, path_lengths[neighbor] + 1);
        }
        path_lengths[node] = max;
    }

    return *std::max_element(path_lengths.cbegin(), path_lengths.cend());
}

namespace {

// Implements Kahn's algorithm
std::vector<size_t> topological_sort(const Graph& graph)
{
    std::vector<size_t> order;
    order.reserve(graph.num_nodes());

    auto in_degree = compute_in_degree(graph);

    auto zero_in_degree_nodes = all_nodes_with_zero_in_degree(in_degree);

    while (!zero_in_degree_nodes.empty()) {
        const size_t current = zero_in_degree_nodes.front();
        order.push_back(current);
        zero_in_degree_nodes.pop();
        for (auto neighbor : graph.neighbors(current)) {
            --in_degree[neighbor];
            if (in_degree[neighbor] == 0)
                zero_in_degree_nodes.push(neighbor);
        }
    }

    // check if in_degree doesn't have all zeroes than graph is not DAG
    if (std::any_of(in_degree.cbegin(), in_degree.cend(), [](size_t incoming) {
            return incoming > 0;
        }))
        throw std::runtime_error{"Graph is not a DAG!"};

    return order;
}

std::vector<size_t> compute_in_degree(const Graph& graph)
{
    std::vector<size_t> in_degree(graph.num_nodes() + 1);
    for (size_t i = 1; i <= graph.num_nodes(); ++i)
        for (auto neighbor : graph.neighbors(i))
            ++in_degree[neighbor];
    return in_degree;
}

std::queue<size_t>
all_nodes_with_zero_in_degree(const std::vector<size_t>& in_degree)
{
    std::queue<size_t> zero_in_degree_nodes;
    for (int i = 1; i < in_degree.size(); ++i)
        if (in_degree[i] == 0)
            zero_in_degree_nodes.push(i);
    return zero_in_degree_nodes;
}

Graph reverse(const Graph& graph)
{
    Graph reversed_graph(graph.num_nodes());
    for (size_t i = 1; i <= graph.num_nodes(); ++i) {
        for (size_t neighbor : graph.neighbors(i)) {
            reversed_graph.add_edge(neighbor, i);
        }
    }
    return reversed_graph;
}

} // namespace

int main(int argc, char* argv[])
{
    size_t num_nodes, num_edges;
    std::cin >> num_nodes >> num_edges;
    Graph graph{num_nodes};
    for (size_t i = 0; i < num_edges; ++i) {
        size_t from, to;
        std::cin >> from >> to;
        graph.add_edge(from, to);
    }
    std::cout << length_of_longest_path(graph) << std::endl;
    return 0;
}


Measured memory_bytes: 12784