CodeEff/ECCO huggingface dataset:
# Original solution
#!/usr/bin/env python
import sys
import re
from collections import deque, defaultdict
from itertools import combinations, combinations_with_replacement
import bisect
from math import factorial, sqrt, log, ceil, floor
sys.setrecursionlimit(1000000)


def get_longest(x, longest, edges):
    if longest[x] is not None:
        return longest[x]
    if not edges[x]:
        return 0
    result = max([get_longest(y, longest, edges)
                  for y in edges[x]])
    return result + 1


def main():
    input = sys.stdin.readline
    #N, M = map(int, input().split())
    #N, K = map(int, input().split())
    #ps = list(map(int, input().split()))

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

    edges = defaultdict(set)
    for i in range(M):
        x, y = list(map(int, input().split()))
        edges[x].add(y)

    longest = [None] * (N + 1)
    for x in range(N):
        x += 1
        if longest[x] is None:
            longest[x] = get_longest(x, longest, edges)

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


if __name__ == '__main__':
    main()


# Optimized solution
#!/usr/bin/env python
import sys
import re
from collections import deque, defaultdict
from itertools import combinations, combinations_with_replacement
import bisect
from math import factorial, sqrt, log, ceil, floor
sys.setrecursionlimit(1000000)


def get_longest(x, longest, edges):
    if longest[x] is not None:
        return longest[x]
    if not edges[x]:
        longest[x] = 0
    else:
        result = max([get_longest(y, longest, edges)
                      for y in edges[x]])
        longest[x] = result + 1
    return longest[x]


def main():
    input = sys.stdin.readline
    #N, M = map(int, input().split())
    #N, K = map(int, input().split())
    #ps = list(map(int, input().split()))

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

    edges = defaultdict(set)
    for i in range(M):
        x, y = list(map(int, input().split()))
        edges[x].add(y)

    longest = [None] * (N + 1)
    for x in range(N):
        x += 1
        if longest[x] is None:
            get_longest(x, longest, edges)

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


if __name__ == '__main__':
    main()
# Original solution
import sys

sys.setrecursionlimit(10**9)
n,m=list(map(int,input().split()))

g=[[] for i in range(n)]
for i in range(m):
  v1,v2=list(map(int,input().split()))
  v1-=1;v2-=1
  g[v1].append(v2)

def dfs(i):
  temp=0
  for nv in g[i]:
    temp=max(temp,dfs(nv)+1)
    
  return temp  
    
res=0
for i in range(n):
  res=max(res,dfs(i))
print(res)
  
  

# Optimized solution
import sys

sys.setrecursionlimit(10**9)
n,m=list(map(int,input().split()))

g=[[] for i in range(n)]
for i in range(m):
  v1,v2=list(map(int,input().split()))
  v1-=1;v2-=1
  g[v1].append(v2)

dp=[-1]*n  
  
def dfs(i):
  if dp[i]!=-1:
    return dp[i]
  temp=0
  for nv in g[i]:
    temp=max(temp,dfs(nv)+1)
  dp[i]=temp  
  return temp  
    
res=0
for i in range(n):
  res=max(res,dfs(i))
print(res)
# Original solution
from collections import deque

N, M = list(map(int, input().split()))
G = [[] for _ in range(N)]
memo =  [[0] * N for _ in range(N)] #矢印が入ってきている数字を-1でフラグ立てる
dp = [0] * N
for _ in range(M):
    x, y = list(map(int, input().split()))
    x -= 1
    y -= 1
    G[x].append((x, y))
    memo[y][x] = 1

q = deque()

for i in range(N):
    q.append(G[i])
    while q:
        moves = q.pop()
        for move in moves:
            x, y = move
            dp[y] = max(dp[y], dp[x]+1)
            memo[y][x] = 0
            q.append(G[y])

print((max(dp)))


# Optimized solution
from collections import deque

N, M = list(map(int, input().split()))
G = [[] for _ in range(N)]
# memo =  [[0] * N for _ in range(N)] #矢印が入ってきている数字を-1でフラグ立てる
dp = [0] * N
for _ in range(M):
    x, y = list(map(int, input().split()))
    x -= 1
    y -= 1
    G[x].append((x, y))
    # memo[y][x] = 1

q = deque()

for i in range(N):
    q.append(G[i])
    while q:
        moves = q.pop()
        for move in moves:
            x, y = move
            dp[y] = max(dp[y], dp[x]+1)
            # memo[y][x] = 0
            q.append(G[y])

print((max(dp)))
# Original solution
import sys

sys.setrecursionlimit(10**9)

n , m = list(map(int,input().split()))

g = [[] for i in range(n+1)]

dp = [-1] * (n+1)
for _ in range(m):
    v1 , v2 = list(map(int,input().split()))
    g[v1].append(v2)
    
# finding longest path for vertice i
def dfs(i):
    if dp[i]!=-1:
        return dp[i]
    temp = 0
    for nv in g[i]:
        temp = max(temp, dfs(nv)+1)

    dp[i]=temp
    return temp
  
# finding overall longest path  
res = 0  
for i in range(1,n+1):
    res = max(res, dfs(i))
print(res)

# Optimized solution
import sys
N, M = list(map(int, input().split()))

sys.setrecursionlimit(10**9)

vertices = [[] for i in range(N+1)]
dp = [-1] * (N+1)

for _ in range(M):
    fro, to = list(map(int, input().split()))
    vertices[fro].append(to)

def dfs(i):
    
    if dp[i] != -1:
        return dp[i]
    
    temp = 0
    for vertex in vertices[i]:
        temp = max(temp, dfs(vertex)+1)
        
    dp[i] = temp
    return temp

for i in range(1,N+1):
    dfs(i)
    
print((max(dp)))
# Original solution
N,M = list(map(int,input().split()))
XY = []
for i in range(M):
    xy = list(map(int,input().split()))
    XY.append(xy)
p = [i+1 for i in range(N)]
ans = []
stack = []
alr = []
xy1 = []
for xy in XY:
    xy1.append(xy[1])
for i in range(1,N+1):
    if i not in xy1:
        stack.append(i)
        alr.append(i)

while len(stack) > 0:
    p1 = stack.pop(-1)
    flag = True
    for xy in XY:
        if p1 == xy[0]:
            alr.append(xy[1])
            stack.append(xy[1])
        if (p1 == xy[1]) and (xy[0] not in ans):
            flag = False
    if flag:
        ans.append(p1)

    
dp = [0 for i in range(len(ans))]

for i in range(len(ans)):
    for j in range(i):
        if [ans[j],ans[i]] in XY:
            dp[i] = max(dp[i],dp[j]+1)


print((max(dp)))        


# Optimized solution
import sys
sys.setrecursionlimit(10**5+10)
input = sys.stdin.readline

N,M = list(map(int,input().split()))
XY = [[] for m in range(N)]
for l in range(M):
    x,y = list(map(int,input().split()))
    XY[x-1].append(y-1)

dp = [-1 for k in range(N)]
def search(i):
    if dp[i] != -1:
        return dp[i]
    c = 0
    start = XY[i]
    for s in start:
        c = max(c,search(s)+1)
    dp[i] = c
    return dp[i]

a = 0
for i in range(N):
    a = max(a,search(i))
print(a)
# Original solution
import collections
import sys
sys.setrecursionlimit(10000000)

N,M = list(map(int,input().split()))
A = [list(map(int, input().split())) for i in range(M)]
visit = set()
G = collections.defaultdict(set)
for idx,(f,to) in enumerate(A):
    G[f].add(to)

cache = {}
    
def dfs(start):
    if start in cache:
        return cache[start]
    ma = 0
    for i in G[start]:
        if i not in visit:
            visit.add(i)
            ma = max(ma,dfs(i))
            visit.remove(i)
    cache[start] = ma + 1 
    return ma + 1

ma = 0 
for f,to in A:
    visit.add(f)
    ma = max(ma,dfs(f))
    visit.remove(f)
print((ma - 1))
        
        

# Optimized solution
import collections
import sys
sys.setrecursionlimit(10000000)

N,M = list(map(int,input().split()))
A = [list(map(int, input().split())) for i in range(M)]
visit = set()
G = collections.defaultdict(set)
for idx,(f,to) in enumerate(A):
    G[f].add(to)

cache = {}
    
def dfs(start):
    if start in cache:
        return cache[start]
    ma = 0
    for i in G[start]:
        if i not in visit:
            #visit.add(i)
            ma = max(ma,dfs(i))
            #visit.remove(i)
    cache[start] = ma + 1 
    return ma + 1

ma = 0 
for f,to in A:
    #visit.add(f)
    ma = max(ma,dfs(f))
    #visit.remove(f)
print((ma - 1))
# Original solution
import sys

sys.setrecursionlimit(10 ** 7)

input = sys.stdin.readline

inf = float('inf')


def rec(v):
    if used[v]:
        return dp[v]
    used[v] = True
    res = 0
    for u in es[v]:
        res = max(res, rec(u) + 1)
    dp[v] = res
    return res


n, m = list(map(int, input().split()))

es = tuple(set() for _ in range(n))
for _ in range(m):
    x, y = list(map(int, input().split()))
    x -= 1
    y -= 1
    es[x].add(y)

used = [False] * n
dp = [inf] * n

ans = 0
for i in range(n):
    ans = max(ans, rec(i))
print(ans)


# Optimized solution
import sys

sys.setrecursionlimit(10 ** 7)


def dfs(v):
    if d[v] != -1:
        return d[v]
    res = -1
    for u in e[v]:
        res = max(res, dfs(u))
    res += 1
    d[v] = res
    return res


n, m = list(map(int, input().split()))
e = tuple(set() for _ in range(n))
for _ in range(m):
    x, y = list(map(int, input().split()))
    x -= 1
    y -= 1
    e[x].add(y)

d = [-1] * n
res = -1
for v in range(n):
    res = max(res, dfs(v))
print(res)
# Original solution
def main():
    from functools import lru_cache
    import sys
    sys.setrecursionlimit(10 ** 7)
    readline = sys.stdin.readline

    @lru_cache(maxsize=None)
    def dfs(v, par=-1):
        """最長有向パスに含まれる頂点数"""
        ret = 0
        for u in g[v]:
            if u == par: continue
            ret = max(ret, dfs(u, v))
        return ret + 1

    n, m = list(map(int, readline().split()))

    g = tuple(set() for _ in range(n))
    for _ in range(m):
        x, y = (int(c) - 1 for c in input().split())
        g[x].add(y)

    ret = 0
    for i in range(n):
        ret = max(ret, dfs(i))
    print((ret - 1))


if __name__ == '__main__':
    main()


# Optimized solution
def main():
    import sys
    sys.setrecursionlimit(10 ** 7)
    readline = sys.stdin.readline

    def dfs(v):
        """最長有向パスに含まれる頂点数"""
        if memo[v] != -1:
            return memo[v]

        ret = 0
        for u in g[v]:
            ret = max(ret, dfs(u))
        ret += 1  # 自身
        memo[v] = ret
        return ret

    n, m = list(map(int, readline().split()))

    g = tuple(set() for _ in range(n))
    for _ in range(m):
        x, y = (int(c) - 1 for c in input().split())
        g[x].add(y)

    memo = [-1] * n

    ret = 0
    for i in range(n):
        ret = max(ret, dfs(i))
    print((ret - 1))


if __name__ == '__main__':
    main()
# Original solution
#!/mnt/c/Users/moiki/bash/env/bin/python
N,M = list(map(int, input().split()))
g = [ list([int(x)-1 for x in input().split()]) for _ in range(M) ]

from collections import Counter, defaultdict
childs  = Counter( x[1] for x in g)
childs_list  = defaultdict(list)

for x,y in g:
    childs_list[x].append(y)
roots = []
for i in range(N):
    if childs[i] == 0:
        roots.append(i)

l = [ i for i in roots]
costs = [ 0 for _ in range(N)]

while l: 

    nex_stack = []
    # p = l.pop()
    for p in l:

        cnt = costs[p]

        for c in childs_list[p]:
            if cnt+1 > costs[c]:
                costs[c] = cnt+1
                # l.append( c ) 
                nex_stack.append(c)
    
    l = nex_stack


print((max(costs)))
        
        



# Optimized solution
#!/mnt/c/Users/moiki/bash/env/bin/python
N,M = list(map(int, input().split()))
g = [ list([int(x)-1 for x in input().split()]) for _ in range(M) ]

from collections import Counter, defaultdict
childs  = Counter( x[1] for x in g)
childs_list  = defaultdict(list)

for x,y in g:
    childs_list[x].append(y)
roots = []
for i in range(N):
    if childs[i] == 0:
        roots.append(i)

l = [ i for i in roots]
costs = [ 0 for _ in range(N)]

while l: 

    nex_stack = []
    # p = l.pop()
    for p in l:
        # cnt = costs[p]
        for c in childs_list[p]:
            costs[c] = max(costs[p]+1, costs[c])
            childs[c] -= 1
            if childs[c] == 0:
                nex_stack.append(c)
    
    l = nex_stack


print((max(costs)))
# Original solution
import sys
from collections import defaultdict
readline = sys.stdin.buffer.readline
sys.setrecursionlimit(10**6)


def geta(fn=lambda s: s.decode()):
    return list(map(fn, readline().split()))


def gete(fn=lambda s: s.decode()):
    return fn(readline().rstrip())


def main():
    N, M = geta(int)
    g = defaultdict(lambda: [])
    for _ in range(M):
        x, y = geta(int)
        g[x].append(y)

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

    def dfs(i):
        if dp[i] != -1:
            return dp[i]
        else:
            dist = 0
            for j in g[i]:
                dist_j = 1 + dfs(j)
                if dist_j > dist: dist = dist_j
            return dist

    ans = 0
    for i in range(1, N + 1):
        tmp = dfs(i)
        if tmp > ans: ans = tmp

    print(ans)


if __name__ == "__main__":
    main()

# Optimized solution
import sys
from collections import defaultdict
readline = sys.stdin.buffer.readline
sys.setrecursionlimit(10**6)


def geta(fn=lambda s: s.decode()):
    return list(map(fn, readline().split()))


def gete(fn=lambda s: s.decode()):
    return fn(readline().rstrip())


def main():
    N, M = geta(int)
    g = defaultdict(lambda: [])
    for _ in range(M):
        x, y = geta(int)
        g[x].append(y)

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

    def dfs(i):
        if dp[i] == -1:
            dist = 0
            for j in g[i]:
                dist_j = 1 + dfs(j)
                if dist_j > dist: dist = dist_j
            dp[i] = dist
        return dp[i]

    ans = 0
    for i in range(1, N + 1):
        tmp = dfs(i)
        if tmp > ans: ans = tmp

    print(ans)


if __name__ == "__main__":
    main()
# Original solution
from collections import deque
N,M = list(map(int, input().split()))
edge=[[] for j in range(N+1)]
x = [0] * M
y = [0] * M

#N,M=4,5
#x=[1, 1, 3, 2, 3]
#y=[2, 3, 2, 4, 4]
#edge=[[], [2, 3], [4], [2, 4], []]  

for i in range(M):
    x[i], y[i] = list(map(int, input().split()))
    edge[x[i]].append(y[i])

def g(x):
    q=deque([x])
    move=[[1,0],[-1,0],[0,1],[0,-1]]
    num=-1
    s=False
    while q:
        #print(q)
        for i in range(len(q)):
            b=q.popleft()
            if edge[b]==[]:
                #print(num)
                #print(H*W-kuro-num-1)
                num=num
            else:
                for i in edge[b]:
                    q.append(i)
        num+=1
    return num        
m=0
for i in range(N):
    m=max(m,g(i+1))
print(m)

# Optimized solution
import sys
sys.setrecursionlimit(10**9)
 
 
N, M = list(map(int, input().split()))
 
G = [[] for _ in range(N + 1)]
for i in range(M):
    x, y = list(map(int, input().split()))
    G[x] += [y]
# print(G)
 
dp = [-1] * (N + 1)
 
 
def func(n):
    if dp[n] != -1:
        return dp[n]
    cnt = 0
	# 先にノードが存在する場合
	# ノードが存在しない場合は、cnt=0のままforをすっ飛ばす
    for m in G[n]:
        cnt = max(cnt, func(m) + 1)
    dp[n] = cnt
    return cnt
 
 
for i in range(1, N + 1):
    func(i)
 
# print(dp)
print((max(dp)))
# Original solution
#DAGの最長有向パスをトポロジカルソートで解く
from collections import defaultdict

N,M=list(map(int,input().split(' ')))
se=set()
d=defaultdict(int)
for i in range(M):
    x,y=list(map(int,input().split(' ')))
    se.add(x*(10**5)+y)
n=set(i for i in range(1,N+1))
while N>0:
    m=set([x%10**5 for x in se])
    for i in n-m:
        n.remove(i)
        N-=1
        de=[x for x in se if x//10**5==i]
        for j in de:
            k=j%10**5
            se.remove(j)
            if d[k]<=d[i]:
                d[k]=d[i]+1
print((max(d.values())))

# Optimized solution
from collections import defaultdict, deque

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

outs = defaultdict(list)
ins = defaultdict(int)
for v1, v2 in es:
    outs[v1].append(v2)
    ins[v2] += 1
length=defaultdict(int)

q = deque(set(i for i in range(1,N+1))-set(ins.keys()))
length=defaultdict(int)
while q:
    v1 = q.popleft()
    for v2 in outs[v1]:
        ins[v2] -= 1
        if length[v2]<=length[v1]:
            length[v2]=length[v1]+1
        if ins[v2] == 0:
            q.append(v2)

print((max(length.values())))
# Original solution
#DAGの最長有向パスをトポロジカルソートで解く
from collections import defaultdict

N,M=list(map(int,input().split(' ')))
se=set()
d=defaultdict(int)
for i in range(M):
    x,y=list(map(int,input().split(' ')))
    se.add(x*(10**5)+y)
n=set(i for i in range(1,N+1))
while N>0:
    m=set([x%10**5 for x in se])
    for i in n-m:
        n.remove(i)
        N-=1
        de=set([x for x in se if x//10**5==i])
        for j in de:
            k=j%10**5
            se.remove(j)
            if d[k]<=d[i]:
                d[k]=d[i]+1
print((max(d.values())))

# Optimized solution
from collections import defaultdict

n,m=list(map(int,input().split(' ')))
paths=defaultdict(list)
ins=[0]*(n+1)
for _ in range(m):
    x,y=list(map(int,input().split(' ')))
    paths[x].append(y)
    ins[y]+=1

no_in=[y for y in range(1,n+1) if ins[y]==0]
depth=[0]*(n+1)
while no_in:
    x=no_in.pop()
    for y in paths[x]:
        ins[y]-=1
        depth[y]=max(depth[y],depth[x]+1)
        if ins[y]==0:
            no_in.append(y)

print((max(depth)))
# Original solution
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**7)

n, m = [int(x) for x in input().split()]
g = [[] for _ in range(n)]
for _ in range(m):
    x, y = [int(x) for x in input().split()]
    g[x - 1].append(y - 1)

memo = [-1]*n

def dp(v):
    if memo[v] >= 0:
        return memo[v]
    else:
        res = 0
        for u in g[v]:
            res = max(res, dp(u) + 1)
        return res

for i in range(n):
    memo[i] = dp(i)
print((max(memo)))




# Optimized solution
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**7)

n, m = [int(x) for x in input().split()]
g = [[] for _ in range(n)]
for _ in range(m):
    x, y = [int(x) for x in input().split()]
    g[x - 1].append(y - 1)

memo = [-1]*n

def dp(v):
    if memo[v] >= 0:
        return memo[v]
    else:
        res = 0
        for u in g[v]:
            res = max(res, dp(u) + 1)
        memo[v] = res
        return res

for i in range(n):
    dp(i)
print((max(memo)))
# Original solution
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**7)

n, m = [int(x) for x in input().split()]
g = [[] for _ in range(n)]
for _ in range(m):
    x, y = [int(x) for x in input().split()]
    g[x - 1].append(y - 1)

memo = [-1]*n

def dp(v):
    if memo[v] >= 0:
        return memo[v]
    else:
        res = 0
        for u in g[v]:
            res = max(res, dp(u) + 1)
        memo[v] = res
        return res

for i in range(n):
    dp(i)
print((max(memo)))




# Optimized solution
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**7)

n, m = [int(x) for x in input().split()]
g = [[] for _ in range(n)]
for _ in range(m):
    x, y = [int(x) for x in input().split()]
    g[x - 1].append(y - 1)

memo = [0]*n
visited = [False]*n

def dp(v):
    if visited[v]:
        return memo[v]
    else:
        res = 0
        for u in g[v]:
            res = max(res, dp(u) + 1)
        memo[v] = res
        visited[v] = True
        return res

for i in range(n):
    dp(i)
print((max(memo)))
# Original solution
import copy
N,M = list(map(int, input().split()))
t_table = {}
for i in range(M):
	x,y = list(map(int, input().split()))
	if x-1 in t_table: t_table[x-1].append(y-1)
	else: t_table[x-1] = [y-1]
ans = 0
dp = [1 for _ in range(N)]
zerolist = [0 for _ in range(N)]
while True:
	pnode = copy.copy(dp)
	dp = [0 for _ in range(N)]
	for i in range(N):
		if pnode[i] == 1 and i in t_table:
			for j in t_table[i]:
				if dp[j]==0 : dp[j]= 1
	if(dp == zerolist):
	 	print(ans)
	 	break
	ans += 1


# Optimized solution
import sys

sys.setrecursionlimit(10**5+10)
input = sys.stdin.readline

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

dp = [-1] * N

def rec(v):
	if dp[v] != -1:
		return dp[v]
	ans = 0
	lst_nv = lst_edge[v]
	for nv in lst_nv:
		ans = max(ans, rec(nv)+1)
	dp[v] = ans
	return dp[v]

ans = 0
for v in range(N):
	ans = max(ans,rec(v))
print(ans)
# Original solution
import sys
sys.setrecursionlimit(10**9)
n,m = list(map(int,input().split()))
graph = [[] for _ in range(n)]
for i in range(m):
  x,y = list(map(int,input().split()))
  graph[x-1].append(y-1)
dp = [0]*(n)

done = []
def dfs(i):
    if i in done:
        done.pop()
        if dp[i] < len(done):dp[i] = len(done)
        return len(done)
    for j in graph[i]:
        if not(j in done):
            done.append(i)
            dfs(j)
    if dp[i] < len(done):dp[i] = len(done)
    if done:done.pop()
    return len(done)

for i in range(n):  
    dfs(i)
print((max(dp)))


# Optimized solution
import sys
sys.setrecursionlimit(10**9)
n,m = list(map(int,input().split()))
graph = [[] for _ in range(n)]
for i in range(m):
  x,y = list(map(int,input().split()))
  graph[x-1].append(y-1)
dp = [-1]*(n)
 
def dfs(i):
    if dp[i] != -1:
        return dp[i]
    else:
        res = 0
        for j in graph[i]:
            res = max(res,dfs(j)+1)
        dp[i] = res
        return dp[i]
 
for i in range(n):  
    dfs(i)
print((max(dp)))
# Original solution
import sys
input = sys.stdin.buffer.readline
from collections import deque

def main():
    N,M = list(map(int,input().split()))
    
    edge = [[] for _ in range(N)]
    begin = [True for _ in range(N)]
    for _ in range(M):
        x,y = list(map(int,input().split()))
        edge[x-1].append(y-1)
        begin[y-1] = False
            
    def longest(s):
        INF = 10**10
        l = [-INF for _ in range(N)]
        l[s] = 0
        q = deque([s])
        while q:
            now = q.popleft()
            for fol in edge[now]:
                if l[fol] < l[now]+1:
                    l[fol] = l[now]+1
                    q.append(fol)
        return max(l)
    
    ans = 0
    for i,b in enumerate(begin):
        if b:
            ans = max(ans,longest(i))
            
    print(ans)
        
if __name__ == "__main__":
    main()


# Optimized solution
import sys
input = sys.stdin.buffer.readline
sys.setrecursionlimit(10 ** 9)

def main():
    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)
        
    dist = [-1 for _ in range(N)]
            
    def dfs(s):
        if dist[s] != -1:
            return dist[s]
        else:
            ret = 0
            for fol in edge[s]:
                ret = max(ret,dfs(fol)+1)
            dist[s] = ret
            return ret

    for i in range(N):
        dfs(i)
        
    print((max(dist)))
    
if __name__ == "__main__":
    main()
# Original solution
import sys
from functools import lru_cache
sys.setrecursionlimit(524287)

n,m,*l = list(map(int,open(0).read().split()))
pas = [[] for _ in range(n)]
for x,y in zip(l[::2],l[1::2]):
	pas[x-1].append(y-1)

INF = float("inf")
done = [-INF]*n

@lru_cache(maxsize = None)
def dfs(t):
	if done[t] >= 0:
		pass
	elif pas[t]:
		done[t] = max(dfs(s) for s in pas[t]) + 1
	else:
		done[t] = 0
	return done[t]

print((max(dfs(i) for i in range(n))))

# Optimized solution
import sys
sys.setrecursionlimit(524287)

n,m,*l = list(map(int,open(0).read().split()))
pas = [[] for _ in range(n)]
for x,y in zip(l[::2],l[1::2]):
	pas[x-1].append(y-1)

INF = float("inf")
done = [-INF]*n

def dfs(t):
	if done[t] >= 0:
		pass
	elif pas[t]:
		done[t] = max(dfs(s) for s in pas[t]) + 1
	else:
		done[t] = 0
	return done[t]

print((max(dfs(i) for i in range(n))))
# Original solution
from collections import deque
def bfs(vertices, start):      
    q = deque()
    q.append(start)
    while q:
        u = q.popleft()                          
        for v in vertices[u]:
            if vis[v]==0 or (vis[u]+1>vis[v]) :
                vis[v]=vis[u]+1
                q.append(v)
    return max(vis)
n,m=list(map(int,input().split()))
v=[[] for i in range(n+1)]
for i in range(m):
	x,y=list(map(int,input().split()))
	v[x].append(y)
vis = [0]*(n+1) 
vis[0]=1
m=0
while 0 in vis:
	i=vis.index(0)
	vis[i]=1
	m=max(m,bfs(v,i))
print((m-1))

# Optimized solution
import sys
input=sys.stdin.readline
#for _ in range(int(input())):
#	n=int(input())
#	n,m=map(int,input().split())
#	s=input().strip()
sys.setrecursionlimit(10**9)
def dfs(i):
	if dp[i]!=-1:return dp[i]
	c=0
	for j in v[i]:c=max(c,dfs(j)+1)
	dp[i]=c
	return c

n,m=list(map(int,input().split()))
v=[[] for i in range(n+1)]
for i in range(m):
	x,y=list(map(int,input().split()))
	v[x].append(y)
dp = [-1]*(n+1) 
m=0
for i in range(1,n+1):
	m=max(m,dfs(i))
print(m)