CodeEff/ECCO huggingface dataset:
# Original solution
import sys
sys.setrecursionlimit(100000)
N, M = list(map(int, input().split()))
edge = [[] for _ in range(N)]
for i in range(M):
    a,b = list(map(int, input().split()))
    edge[a-1].append(b-1)
dpv = [-1]*N

def dp(v):
    if dpv[v] != -1:
        return dpv[v]
    if len(edge[v])==0:
        dpv[v]=0
        return 0
    res = 0
    for w in edge[v]:
        res = max(dp(w),res)
    res += 1
    dpv[v] = res
    return res

def solve():
    for i in range(N):
        dp(i)
    ans = max(dpv)
    # print(dpv)
    return ans
print((solve()))

# Optimized solution
import sys
sys.setrecursionlimit(10**8)

N, M = list(map(int, input().split()))
edge = [[] for _ in range(N)]
for i in range(M):
  a,b = list(map(int, input().split()))
  edge[a-1].append(b-1)

def dfs(v):
  if len(edge[v])==0:
    return 0
  ans = 0
  for u in edge[v]:
    if longest[u]==-1:
      longest[u]=dfs(u)
    ans = max(ans,longest[u]+1)
  return ans

longest = [-1]*N
for i in range(N):
  if longest[i]==-1:
    longest[i] = dfs(i)
ans = max(longest)
print(ans)
# Original solution
N, M = list(map(int, input().split()))
edge = [[] for _ in range(N)]
to = [False]*N
for i in range(M):
  a,b = list(map(int, input().split()))
  edge[a-1].append(b-1)
  to[b-1] = True

import sys
sys.setrecursionlimit(10**8)

def dfs(v):
  if lis[v]>=0:
    return lis[v]
  if len(edge[v])==0:
    lis[v] = 1
    return 1
  ans = 0
  for u in edge[v]:
    ans = max(ans, dfs(u))
  lis[v] = ans+1
  return ans+1

ans = 0
lis = [-1]*N
for r in range(N):
  if to[r]==False:
    ans = max(ans, dfs(r)-1)
print(ans)

# Optimized solution
import sys
input = sys.stdin.readline

N, M = list(map(int, input().split()))
edge = [[] for _ in range(N)]
for i in range(M):
  a,b = list(map(int, input().split()))
  edge[a-1].append(b-1)

def dfs(start):
  stack = [start]
  while stack:
    v = stack[-1]
    marker = 0
    path_l[v] = 0
    for u in edge[v]:
      if path_l[u]==-1: #子へ降ろす
        marker = 1
        stack.append(u)
      else: #子から吸い上げる
        #吸い上げる際の個々の処理
        path_l[v] = max(path_l[v], path_l[u]+1)
    if marker==0:
      stack.pop()
  return

path_l = [-1]*N
for i in range(N):
  if path_l[i]==-1:
    dfs(i)
ans = max(path_l)
print(ans)
# Original solution
from collections import defaultdict, deque

v, n = list(map(int, input().split()))
es = [[int(x) for x in input().split()] for _ in range(n)]

outs = defaultdict(list)
ins = defaultdict(int)
#print(ins)
for v1, v2 in es:
    outs[v1].append(v2)
    ins[v2] += 1
    #print(ins)
#print("be",ins)
q = deque(v1 for v1 in range(v+1) if ins[v1] == 0)
#print("af",ins)
res = []
#print(outs)
#print(ins)
while q:
    #print(q)
    v1 = q.popleft()
    res.append(v1)
    for v2 in outs[v1]:
        #print("-->",v2)
        ins[v2] -= 1
        if ins[v2] == 0:
            #print("<--",v2)
            q.append(v2)
#print(res[1:])
#print(outs)
#print(ins)

dp = [0]*(v+1)
for i in res[1:]:
    for j in outs[i]:
        dp[j] = dp[i] + 1
        #print(i, j, dp)

print((max(dp)))

# Optimized solution
"""memo
    トポロジカルソート「http://zehnpaard.hatenablog.com/entry/2018/06/26/201512」
    をしてから，dpで求めた．（ソートと同時にdp求められそう）
"""
from collections import deque, defaultdict
import sys
input == sys.stdin.readline

def main():
    v, n = list(map(int, input().split()))
    es = [[int(x) for x in input().split()] for _ in range(n)]

    outs = defaultdict(list)
    ins = defaultdict(int)
    #print(ins)
    for v1, v2 in es:
        outs[v1].append(v2)
        ins[v2] += 1
        #print(ins)
    #print("be",ins)
    q = deque(v1 for v1 in range(1,v+1) if ins[v1] == 0) #rangeは無駄なものがない
    #print("af",ins)
    res = []
    #print(outs)
    #print(ins)
    dp = [0]*(v+1)
    while q:
        #print(q)
        v1 = q.popleft()
        res.append(v1)
        for v2 in outs[v1]: #v1のノードから出ている矢印をみる．
            dp[v2] = dp[v1] + 1
            #print("-->",v2)
            ins[v2] -= 1
            if ins[v2] == 0: #v2のノードに入る矢印をすべて見たときにqに追加
                #print("<--",v2)
                q.append(v2)
    #print(res[1:])
    #print(outs)
    #print(ins)

    print((max(dp)))

if __name__ == '__main__':
    main()
# Original solution
import sys
sys.setrecursionlimit(10**7)

n, m = list(map(int, input().split()))
edge = [[] for _ in range(n)]
for _ in range(m):
    x, y = list(map(int, input().split()))
    edge[x-1].append(y-1)
dp = [-1]*n
    
def calc(x):
    if dp[x] != -1:
        return dp[x]
    if not len(edge[x]):
        dp[x] = 0
        return 0
    dp[x] = max(calc(i) for i in edge[x])+1
    return dp[x]
    
ans = 0
for i in range(n):
    if dp[i] != -1: continue
    ans = max(ans, calc(i))
print(ans)

# Optimized solution
import sys
sys.setrecursionlimit(10**7)

N, M = list(map(int, input().split()))
edge = [[] for _ in range(N)]
for _ in range(M):
    x, y = list(map(int, input().split()))
    edge[x-1].append(y-1)
dp = [-1]*N

def calc(x):
    if dp[x]!=-1:
        return dp[x]
    if not len(edge[x]):
        dp[x] = 0
        return 0
    dp[x] = max([calc(i) for i in edge[x]])+1
    return dp[x]

ans = 0
for i in range(N):
    if dp[i] != -1: continue
    ans = max(ans, calc(i))
print(ans)
# Original solution
import collections
N,M = list(map(int,input().split()))
road = [[] for i in range(N+1)]
rev_road = [[] for i in range(N+1)]
for _ in range(M):
    x,y=list(map(int,input().split()))
    road[x].append(y)
    rev_road[y].append(x)

dep = []
for i in range(1,N+1):
    if len(rev_road[i]) == 0:
        dep.append(i)

def largest_distance(i):
    q = collections.deque([i])
    dp =[0]*(N+1) #地点iからの最長経路
    dp_max = 0
    while q:
        now = q.popleft()
        for direct in road[now]:
            if dp[direct]<=dp[now]:
                dp[direct] = dp[now]+1
                q.append(direct)
                dp_max = max(dp_max,dp[direct])
    return dp_max
ans = 0
for de in dep:
    ans = max(ans,largest_distance(de))
print(ans)


# Optimized solution
import sys
sys.setrecursionlimit(10**9)
N,M = list(map(int,input().split()))
road = [[] for _ in range(N+1)]
for _ in range(M):
    x,y=list(map(int,input().split()))
    road[x].append(y)
flag = [0]*(N+1)
dist = [0]*(N+1)
def distance(i):#地点iから終点までの最長距離
    if flag[i] == 1:
        return dist[i]
    
    if road[i] == []:#行き先がない
        return 0
    else:#行き先がある
        long_dist = 0
        tmp = 0
        for dirct in road[i]:
            tmp = 1 + distance(dirct)
            long_dist = max(long_dist,tmp)
        flag[i]=1
        dist[i] = long_dist
        return long_dist

ans = 0
for i in range(1,N+1):
    ans = max(ans,distance(i))
print(ans)
# Original solution
import sys
from collections import deque
sys.setrecursionlimit(10**9)
INF=10**18
MOD=10**9+7
def input(): return sys.stdin.readline().rstrip()

def main():
    N,M=list(map(int,input().split()))
    edge=[[] for _ in range(N)]
    s=set(range(N))
    for _ in range(M):
        x,y=[int(x)-1 for x in input().split()]
        edge[x].append(y)
        try:
            s.remove(y)
        except:
            pass
    que=deque(s)
    memo=[0]*N
    ans=0
    while que:
        v=que.popleft()
        for nv in edge[v]:
            memo[nv]=max(memo[nv],memo[v]+1)
            ans=max(ans,memo[nv])
            que.append(nv)
    print(ans)
    
    

if __name__ == '__main__':
    main()


# Optimized solution
import sys
sys.setrecursionlimit(10**9)
INF=10**18
MOD=10**9+7
def input(): return sys.stdin.readline().rstrip()

def main():
    N,M=list(map(int,input().split()))
    edge=[[] for _ in range(N)]
    s=set(range(N))
    for _ in range(M):
        x,y=[int(x)-1 for x in input().split()]
        edge[x].append(y)
        try:
            s.remove(y)
        except:
            pass
    que=list(s)
    memo=[0]*N
    def saiki(v):
        if memo[v]:
            return memo[v]
        tmp=0
        for nv in edge[v]:
            memo[v]=max(memo[v],saiki(nv)+1)
            tmp=max(tmp,memo[v])
        return tmp
    ans=0
    for v in que:
        ans=max(ans,saiki(v))
    print(ans)
    
    

if __name__ == '__main__':
    main()
# Original solution
from sys import stdin, setrecursionlimit
# input = stdin.readline
setrecursionlimit(200000)
N, M = list(map(int, input().split()))
# d = [0] * (N + 1)
adj = [[] for _ in range(N + 1)]
for _ in range(M):
    x, y = list(map(int, input().split()))
    adj[x].append(y)
    # d[y] += 1

dp = [-1] * (N + 1)


def lp(x: 'int') -> 'int':
    if dp[x] >= 0:
        return dp[x]
    else:
        dp[x] = 0
        for y in adj[x]:
            dp[x] = max(dp[x], lp(y) + 1)
        return dp[x]


print((max(lp(i) for i in range(1, N + 1))))


# Optimized solution
from sys import stdin, setrecursionlimit
input = stdin.readline
setrecursionlimit(200000)
N, M = list(map(int, input().split()))
# d = [0] * (N + 1)
adj = [[] for _ in range(N + 1)]
for _ in range(M):
    x, y = list(map(int, input().split()))
    adj[x].append(y)
    # d[y] += 1

dp = [-1] * (N + 1)


def lp(x: 'int') -> 'int':
    if dp[x] >= 0:
        return dp[x]
    else:
        dp[x] = 0
        for y in adj[x]:
            dp[x] = max(dp[x], lp(y) + 1)
        return dp[x]


print((max(lp(i) for i in range(1, N + 1))))
# Original solution
from sys import stdin, setrecursionlimit
from collections import defaultdict
input = stdin.readline
setrecursionlimit(200000)
N, M = list(map(int, input().split()))
# d = [0] * (N + 1)
adj = defaultdict(list)
for _ in range(M):
    x, y = list(map(int, input().split()))
    adj[x].append(y)
    # d[y] += 1

dp = [-1] * (N + 1)


def lp(x: 'int') -> 'int':
    if dp[x] >= 0:
        return dp[x]
    else:
        dp[x] = 0
        for y in adj[x]:
            dp[x] = max(dp[x], lp(y) + 1)
        return dp[x]


print((max(lp(i) for i in range(1, N + 1))))


# Optimized solution
"""
https://atcoder.jp/contests/dp/tasks/dp_g

Using list to store adjacent list is faster than using sets.
Maybe it is because the append operation of list in python is faster than add operation of sets.

Using defaultdict instead of list to store adj is faster. Maybe it is because in case the graph is
sparse, defauldict can save more query time.
"""
from sys import stdin
from collections import defaultdict, deque
input = stdin.readline
N, M = list(map(int, input().split()))
d = [0] * (N + 1)
adj = defaultdict(list)
for _ in range(M):
    x, y = list(map(int, input().split()))
    adj[x].append(y)
    d[y] += 1

dp = [0] * (N + 1)  # dp denotes longest path ends at i

Q = deque([i for i in range(1, N + 1) if d[i] == 0])

while Q:
    cur = Q.popleft()
    for nxt in adj[cur]:
        dp[nxt] = max(dp[nxt], dp[cur] + 1)
        d[nxt] -= 1
        if d[nxt] == 0:
            Q.append(nxt)
print((max(dp)))
# Original solution
import sys
sys.setrecursionlimit(10**8)
N, M = list(map(int, input().split()))
G = dict([[i, []] for i in range(N)])
for _ in range(M):
    x, y = list(map(int, input().split()))
    G[x-1].append(y-1)

dp = [-1] * N


def rec(v):
    if dp[v] >= 0:
        return dp[v]
    if len(G[v]) == 0:
        return 0

    ret = -1
    for nv in G[v]:
        ret = max(ret, rec(nv) + 1)

    return ret


print((max([rec(v) for v in range(N)])))


# Optimized solution
import sys
sys.setrecursionlimit(10**8)
N, M = list(map(int, input().split()))
G = dict([[i, []] for i in range(N)])
for _ in range(M):
    x, y = list(map(int, input().split()))
    G[x-1].append(y-1)

dp = [-1] * N


def rec(v):
    if dp[v] >= 0:
        return dp[v]
    if len(G[v]) == 0:
        return 0

    ret = -1
    for nv in G[v]:
        ret = max(ret, rec(nv) + 1)

    dp[v] = ret
    return ret


print((max([rec(v) for v in range(N)])))
# Original solution
from collections import deque

def main():
    n, m = list(map(int, input().split()))
    graph = [[] for _ in range(n+1)]
    for _ in range(m):
        src, dst = list(map(int, input().split()))
        graph[dst].append(src)

    dist = [0] * (n+1)
    for node in range(1, n+1):
        visited = [False] * (n+1)
        q = deque([node])
        while q:
            at = q.popleft()
            visited[at] = True
            for to in graph[at]:
                dist[to] = max(dist[to], dist[at] + 1)
                if not visited[to]:
                    q.append(to)
    print((max(dist[1:])))


if __name__ == '__main__':
    main()


# Optimized solution
from collections import deque

def main():
    n, m = list(map(int, input().split()))
    graph = [[] for _ in range(n+1)]
    parents = [0] * (n+1)
    for _ in range(m):
        src, dst = list(map(int, input().split()))
        graph[src].append(dst)
        parents[dst] += 1

    q = deque()
    for i in range(1, n+1):
        if parents[i] == 0:
            q.append(i)

    dists = [0] * (n+1)
    for i in q:
        dists[0] = 0

    while q:
        at = q.popleft()
        for to in graph[at]:
            parents[to] -= 1
            if parents[to] == 0:
                q.append(to)
                dists[to] = dists[at] + 1

    print((max(dists[1:])))


if __name__ == '__main__':
    main()
# Original solution
### 動的計画法のテーブルを用意
# dp(n) は頂点nを始点として時の最大有効パスの長さ

def get_inputs():
    N, M = list(map(int, input().split()))

    # グラフgを初期化
    g = [[] for _ in range(N)]

    # 辺情報を読み込みgに追加
    for _ in range(M):
        x, y = [int(c) - 1 for c in input().split()]
        g[x].append(y)

    return N, M, g


def getlp(x, depth):
    global dp, g

    if depth >= MAX_DEPTH:
        return -1
    elif dp[x] != -1:
        return dp[x]
    elif len(g[x]) == 0:
        dp[x] = 0
        return dp[x]
    else:
        mxp = 0
        for y in g[x]:
            yp = getlp(y, depth + 1)
            if yp == -1:
                return -1
            elif yp > mxp:
                mxp = yp
        dp[x] = mxp + 1
        return dp[x]


N, M, g = get_inputs()
MAX_DEPTH = 900

dp = [-1] * N
nodes = list(range(N))
ans = 0

while len(nodes) > 0:
    tmp_nodes = []
    for x in range(N):
        if dp[x] == -1:
            t = getlp(x, 0)
            if t == -1:
                tmp_nodes.append(x)
            else:
                ans = max(ans, t)
        else:
            ans = max(ans, dp[x])

    nodes = list(tmp_nodes)

print(ans)


# Optimized solution
import sys
sys.setrecursionlimit(10**5)

def get_inputs():
    N, M = list(map(int, input().split()))
    
    # グラフgを初期化
    g = [[] for _ in range(N)]
    
    # 辺情報を読み込みgに追加
    for _ in range(M):
        x, y = [int(c) - 1 for c in input().split()]
        g[x].append(y)
    
    return N, M, g


def getlp(x):
    global dp, g
    
    if dp[x] != -1:
        return dp[x]
    elif len(g[x]) == 0:
        dp[x] = 0
        return dp[x]
    else:
        dp[x] = max([getlp(y) for y in g[x]]) + 1
        return dp[x]


N, M, g = get_inputs()

dp = [-1] * N
ans = 0
for x in range(N):
    ans = max(ans, getlp(x))

print(ans)
# Original solution
import sys
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for i in range(N)]
def dp2(ini, i, j): return [[ini]*i for i2 in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for i2 in range(j)] for i3 in range(k)]
#import bisect #bisect.bisect_left(B, a)
#from collections import defaultdict #d = defaultdict(int) d[key] += value

from collections import deque
sys.setrecursionlimit(10**6)

N, M = mi()
x = li2(M)

adj = [[] for i in range(N)]
for i in range(M):
    x[i][0] -= 1
    x[i][1] -= 1
    adj[x[i][0]].append(x[i][1])

flag = [0]*N
ans = deque([])

def dfs(x, ans):
    if flag[x]:
        return None
    else:
        flag[x] = 1
        if adj[x]:
            for a in adj[x]:
                dfs(a, ans)
        #global ans
        ans.appendleft(x)

for i in range(N):
    dfs(i, ans)

dp = [0]*N
for a in ans:
    if adj[a]:
        for b in adj[a]:
            dp[b] = max(dp[b], dp[a] + 1)

print((max(dp)))

# Optimized solution
import sys
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for i in range(N)]
def dp2(ini, i, j): return [[ini]*i for i2 in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for i2 in range(j)] for i3 in range(k)]
#import bisect #bisect.bisect_left(B, a)
#from collections import defaultdict #d = defaultdict(int) d[key] += value

from collections import deque
sys.setrecursionlimit(10**6)

N, M = mi()
x = li2(M)

adj = [[] for i in range(N)]
for i in range(M):
    x[i][0] -= 1
    x[i][1] -= 1
    adj[x[i][0]].append(x[i][1])

flag = [0]*N
ans = deque([])

def dfs(x):
    if flag[x]:
        return None
    else:
        flag[x] = 1
        for a in adj[x]:
            dfs(a)
        global ans
        ans.appendleft(x)

for i in range(N):
    dfs(i)

dp = [0]*N
for a in ans:
    for b in adj[a]:
        dp[b] = max(dp[b], dp[a] + 1)

print((max(dp)))
# Original solution
import sys
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for i in range(N)]
def dp2(ini, i, j): return [[ini]*i for i2 in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for i2 in range(j)] for i3 in range(k)]
#import bisect #bisect.bisect_left(B, a)
#from collections import defaultdict #d = defaultdict(int) d[key] += value

from collections import deque
sys.setrecursionlimit(10**6)

N, M = mi()
x = li2(M)

adj = [[] for i in range(N)]
for i in range(M):
    x[i][0] -= 1
    x[i][1] -= 1
    adj[x[i][0]].append(x[i][1])

flag = [0]*N
ans = deque([])

def dfs(x):
    if flag[x]:
        return None
    else:
        flag[x] = 1
        for a in adj[x]:
            dfs(a)
        global ans
        ans.appendleft(x)

for i in range(N):
    dfs(i)

dp = [0]*N
for a in ans:
    for b in adj[a]:
        dp[b] = max(dp[b], dp[a] + 1)

print((max(dp)))

# Optimized solution
import sys
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for i in range(N)]
def dp2(ini, i, j): return [[ini]*i for i2 in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for i2 in range(j)] for i3 in range(k)]
#import bisect #bisect.bisect_left(B, a)
#from collections import defaultdict #d = defaultdict(int) d[key] += value

sys.setrecursionlimit(10**6)

def rec(x):
    global dp
    if dp[x]!=-1:
        return dp[x]
    num = 0
    for a in adj[x]:
        num = max(num, rec(a)+1)
    dp[x] = num
    return num


#入力受け取り
N, M = mi()
x = li2(M)

#隣接リスト作成
adj = [[] for i in range(N)]
for i in range(M):
    x[i][0] -= 1
    x[i][1] -= 1
    adj[x[i][0]].append(x[i][1])

dp = [-1]*N
for i in range(N):
    rec(i)

print((max(dp)))
# Original solution
import sys
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for i in range(N)]
def dp2(ini, i, j): return [[ini]*i for i2 in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for i2 in range(j)] for i3 in range(k)]
#import bisect #bisect.bisect_left(B, a)
#from collections import defaultdict #d = defaultdict(int) d[key] += value

sys.setrecursionlimit(10**8)

#メモ化再帰
def rec(x):
    global dp
    if dp[x]!=-1:
        return dp[x]
    num = 0
    for a in adj[x]:
        num = max(num, rec(a)+1)
    dp[x] = num
    return num

#入力受け取り
N, M = mi()
x = li2(M)

#隣接リスト作成
adj = [[] for i in range(N)]
for i in range(M):
    x[i][0] -= 1
    x[i][1] -= 1
    adj[x[i][0]].append(x[i][1])

#メモ化再帰を実行
dp = [-1]*N
for i in range(N):
    rec(i)

print((max(dp)))

# Optimized solution
import sys
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for i in range(N)]
def dp2(ini, i, j): return [[ini]*i for i2 in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for i2 in range(j)] for i3 in range(k)]
#import bisect #bisect.bisect_left(B, a)
#from collections import defaultdict #d = defaultdict(int) d[key] += value

sys.setrecursionlimit(10**9)

#メモ化再帰
def rec(x):
    global dp
    if dp[x]!=-1:
        return dp[x]
    num = 0
    for a in adj[x]:
        num = max(num, rec(a)+1)
    dp[x] = num
    return num

#入力受け取り
N, M = mi()
x = li2(M)

#隣接リスト作成
adj = [[] for i in range(N)]
for i in range(M):
    x[i][0] -= 1
    x[i][1] -= 1
    adj[x[i][0]].append(x[i][1])

#メモ化再帰を実行
dp = [-1]*N
for i in range(N):
    rec(i)

print((max(dp)))
# Original solution
import sys
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for i in range(N)]
def dp2(ini, i, j): return [[ini]*i for i2 in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for i2 in range(j)] for i3 in range(k)]
 
from collections import deque
N, M = mi()
x = li2(M)
 
dp = [[0, []] for i1 in range(N+1)]
 
tmp = deque([])
 
for i in range(M):
    dp[x[i][1]][0] = max(dp[x[i][1]][0], dp[x[i][0]][0] + 1)
    dp[x[i][0]][1].append(x[i][1])
    tmp.append(x[i][1])
    while tmp != deque():
        a = tmp.popleft()
        if dp[a][1] != []:
            for b in dp[a][1]:
                dp[b][0] = max(dp[a][0] + 1, dp[b][0])
                tmp.append(b)
 
#print(dp)
print((max([row[0] for row in dp]))) 

# Optimized solution
import sys
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for i in range(N)]
def dp2(ini, i, j): return [[ini]*i for i2 in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for i2 in range(j)] for i3 in range(k)]
#import bisect #bisect.bisect_left(B, a)
#from collections import defaultdict #d = defaultdict(int) d[key] += value

sys.setrecursionlimit(10**8)

#メモ化再帰
def rec(x):
    global dp
    if dp[x]!=-1:
        return dp[x]
    num = 0
    for a in adj[x]:
        num = max(num, rec(a)+1)
    dp[x] = num
    return num

#入力受け取り
N, M = mi()
x = li2(M)

#隣接リスト作成
adj = [[] for i in range(N)]
for i in range(M):
    x[i][0] -= 1
    x[i][1] -= 1
    adj[x[i][0]].append(x[i][1])

#メモ化再帰を実行
dp = [-1]*N
for i in range(N):
    rec(i)

print((max(dp)))
# Original solution
import sys
import collections
from functools import lru_cache

xy = []
d = collections.defaultdict(list)
@lru_cache(maxsize=None)
def alg_memoization_recursion(f):
    mt = 0
    for v in d[f]:
        mt = max(mt, alg_memoization_recursion(v) + 1)
    return mt


def solve():
    readline = sys.stdin.buffer.readline
    mod = 10 ** 9 + 7
    sys.setrecursionlimit(10 ** 5 + 1)
    n, m = list(map(int, readline().split()))
    for i in range(m):
        x, y = list(map(int, readline().split()))
        d[x-1] += [y-1]
    mt = 0
    for i in range(n):
        mt = max(mt, alg_memoization_recursion(i))
    print(mt)


if __name__ == '__main__':
    solve()


# Optimized solution
import sys
import collections
from functools import lru_cache

sys.setrecursionlimit(10 ** 7 + 1)
xy = []
d = collections.defaultdict(list)

@lru_cache(maxsize=None)
def alg_memoization_recursion(f):
    mt = 0
    for v in d[f]:
        mt = max(mt, alg_memoization_recursion(v) + 1)
    return mt


def solve():
    readline = sys.stdin.buffer.readline
    mod = 10 ** 9 + 7
    n, m = list(map(int, readline().split()))
    for i in range(m):
        x, y = list(map(int, readline().split()))
        d[x-1] += [y-1]
    mt = 0
    for i in range(n):
        mt = max(mt, alg_memoization_recursion(i))
    print(mt)


if __name__ == '__main__':
    solve()