Problem:
Score : 100 points

Problem Statement

There is a directed graph G with N vertices and M edges.
The vertices are numbered 1, 2, \ldots, N, and for each i (1 \leq i \leq M), the i-th directed edge goes from Vertex x_i to y_i.
G does not contain directed cycles.

Find the length of the longest directed path in G.
Here, the length of a directed path is the number of edges in it.

Constraints

All values in input are integers.

2 \leq N \leq 10^5

1 \leq M \leq 10^5

1 \leq x_i, y_i \leq N

All pairs (x_i, y_i) are distinct.

G does not contain directed cycles.

Input

Input is given from Standard Input in the following format:

N M
x_1 y_1
x_2 y_2
:
x_M y_M

Output

Print the length of the longest directed path in G.

Sample Input 1

4 5
1 2
1 3
3 2
2 4
3 4

Sample Output 1

3

The red directed path in the following figure is the longest:

Sample Input 2

6 3
2 3
4 5
5 6

Sample Output 2

2

The red directed path in the following figure is the longest:

Sample Input 3

5 8
5 3
2 3
2 4
5 2
5 1
1 4
4 3
1 3

Sample Output 3

3

The red directed path in the following figure is one of the longest:

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Accepted)

import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.InputMismatchException;
import java.io.IOException;
import java.io.InputStream;

/**
 * Built using CHelper plug-in Actual solution is at the top
 *
 * @author NMouad21
 */
public class Main {
  public static void main(String[] args) {
    InputStream inputStream = System.in;
    OutputStream outputStream = System.out;
    InputReader in = new InputReader(inputStream);
    PrintWriter out = new PrintWriter(outputStream);
    GLongestPath solver = new GLongestPath();
    solver.solve(1, in, out);
    out.close();
  }

  static class GLongestPath {
    public final void solve(int testNumber, InputReader in, PrintWriter out) {
      int n = in.nextInt();
      int m = in.nextInt();
      int[][] e = in.nextIntMatrix(m, 2);
      int[][] g = GraphUtils.packDirectedUnweighted(e, n);
      int[] order = TopologicalSorter.sort(g, n);
      ArrayReverser.reverse(order);

      int ans = 0;
      int[] dp = new int[n + 1];
      for (int u : order) {
        for (int v : g[u]) {
          dp[u] = Math.max(dp[u], dp[v] + 1);
        }
        ans = Math.max(ans, dp[u]);
      }
      out.println(ans);
    }
  }

  static final class TopologicalSorter {
    private TopologicalSorter() {
      throw new RuntimeException("DON'T");
    }

    public static final int[] sort(int[][] g, int n) {
      int[] order = new int[n];
      int addPoint = 0, pollPoint = 0;

      int[] deg = new int[n + 1];
      for (int i = 1; i <= n; i++) {
        for (int v : g[i]) {
          ++deg[v];
        }
      }
      for (int i = 1; i <= n; i++) {
        if (deg[i] == 0) {
          order[addPoint++] = i;
        }
      }

      while (addPoint > pollPoint) {
        int u = order[pollPoint++];
        for (int v : g[u]) {
          if (--deg[v] == 0) {
            order[addPoint++] = v;
          }
        }
      }

      if (addPoint < n) { // cycle detected
        return null;
      }

      return order;
    }
  }

  static final class InputReader {
    private final InputStream stream;
    private final byte[] buf = new byte[1 << 16];
    private int curChar;
    private int numChars;

    public InputReader() {
      this.stream = System.in;
    }

    public InputReader(final InputStream stream) {
      this.stream = stream;
    }

    private final int read() {
      if (this.numChars == -1) {
        throw new UnknownError();
      } else {
        if (this.curChar >= this.numChars) {
          this.curChar = 0;

          try {
            this.numChars = this.stream.read(this.buf);
          } catch (IOException ex) {
            throw new InputMismatchException();
          }

          if (this.numChars <= 0) {
            return -1;
          }
        }

        return this.buf[this.curChar++];
      }
    }

    public final int nextInt() {
      int c;
      for (c = this.read(); isSpaceChar(c); c = this.read()) {}

      byte sgn = 1;
      if (c == 45) { // 45 == '-'
        sgn = -1;
        c = this.read();
      }

      int res = 0;

      while (c >= 48 && c <= 57) { // 48 == '0', 57 == '9'
        res *= 10;
        res += c - 48; // 48 == '0'
        c = this.read();
        if (isSpaceChar(c)) {
          return res * sgn;
        }
      }

      throw new InputMismatchException();
    }

    private static final boolean isSpaceChar(final int c) {
      return c == 32 || c == 10 || c == 13 || c == 9
          || c == -1; // 32 == ' ', 10 == '\n', 13 == '\r', 9 == '\t'
    }

    public final int[][] nextIntMatrix(final int n, final int m) {
      int[][] arr = new int[n][m];
      for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
          arr[i][j] = nextInt();
        }
      }
      return arr;
    }
  }

  static final class GraphUtils {
    private GraphUtils() {
      throw new RuntimeException("DON'T");
    }

    public static final int[][] packDirectedUnweighted(int[][] edges, int n) {
      int[][] g = new int[n + 1][];
      int[] size = new int[n + 1];
      for (int[] edge : edges) {
        ++size[edge[0]];
      }
      for (int i = 0; i <= n; i++) {
        g[i] = new int[size[i]];
      }
      for (int[] edge : edges) {
        g[edge[0]][--size[edge[0]]] = edge[1];
      }
      return g;
    }
  }

  static class ArrayReverser {
    private ArrayReverser() {
      throw new RuntimeException("DON'T");
    }

    public static void reverse(int[] arr, int from, int to) {
      while (from < to) {
        int temp = arr[from];
        arr[from] = arr[to];
        arr[to] = temp;
        ++from;
        --to;
      }
    }

    public static void reverse(int[] arr) {
      reverse(arr, 0, arr.length - 1);
    }
  }
}