Go: package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

var scanner = bufio.NewScanner(os.Stdin)

func nextString() string {
	scanner.Scan()
	return scanner.Text()
}

func nextInt() int {
	n, err := strconv.Atoi(nextString())
	if err != nil {
		fmt.Printf("strconv.Atoi failed: %v\n", err)
	}
	return n
}

var infty = 1000000000

type nodeKey int

type DirectedGraphNode struct {
	key       nodeKey
	adjacents []*DirectedGraphNode
	visited   bool
	d         int
	f         int
	parent    *DirectedGraphNode
}

type DirectedGraph struct {
	nodes []*DirectedGraphNode
}

func (g *DirectedGraph) setAdjacency(l, r nodeKey) {
	g.nodes[l].adjacents = append(g.nodes[l].adjacents, g.nodes[r])
}

func newDirectedGraph(nNodes int) *DirectedGraph {
	graph := DirectedGraph{}
	graph.nodes = make([]*DirectedGraphNode, nNodes)
	for iNodes := 0; iNodes < nNodes; iNodes++ {
		graph.nodes[iNodes] = &DirectedGraphNode{}
		graph.nodes[iNodes].key = nodeKey(iNodes)

		graph.nodes[iNodes].visited = false
		graph.nodes[iNodes].d = infty
		graph.nodes[iNodes].f = infty
		graph.nodes[iNodes].parent = nil
	}
	return &graph
}

type QueueEntry struct {
	node *DirectedGraphNode
	next *QueueEntry
}

type Queue struct {
	head, tail *QueueEntry
}

func (q *Queue) enqueue(node *DirectedGraphNode) {
	entry := QueueEntry{}
	entry.node = node

	if q.tail == nil {
		q.head = &entry
		q.tail = &entry
		return
	}

	q.tail.next = &entry
	q.tail = q.tail.next
}

func (q *Queue) dequeue() *QueueEntry {
	if q.head == nil {
		return nil
	}
	poped := q.head
	q.head = q.head.next
	if q.head == nil {
		q.tail = nil
	}
	return poped
}

func (q *Queue) isEmpty() bool {
	return q.head == nil
}

var time = 0

func fillDistancesByDepthFirstSearch(g *DirectedGraph) {
	for _, u := range g.nodes[1:] {
		if !u.visited {
			dfsVisit(g, u)
		}
	}
}

func dfsVisit(g *DirectedGraph, u *DirectedGraphNode) {
	time++
	u.d = time

	u.visited = true
	for _, v := range u.adjacents {
		if !v.visited {
			v.parent = u
			dfsVisit(g, v)
		}
	}
	time++
	u.f = time
}

func main() {
	scanner.Split(bufio.ScanWords)

	nNodes := nextInt()
	g := newDirectedGraph(nNodes + 1)

	for iNode := 0; iNode < nNodes; iNode++ {
		nodeKeyLeft := nodeKey(nextInt())
		nAdj := nextInt()
		for iAdj := 0; iAdj < nAdj; iAdj++ {
			nodeKeyRight := nodeKey(nextInt())
			g.setAdjacency(nodeKeyLeft, nodeKeyRight)
		}
	}

	fillDistancesByDepthFirstSearch(g)

	for iNode := 1; iNode <= nNodes; iNode++ {
		node := g.nodes[iNode]
		fmt.Println(node.key, node.d, node.f)
	}
}

Python: import collections,sys
def I(): return int(sys.stdin.readline().rstrip())
def LI(): return list(map(int,sys.stdin.readline().rstrip().split()))
n = I()
ukv = [LI() for _ in range(n)]
graph = {i:collections.deque() for i in range(1,n+1)} #1_indexed
for x in ukv:
    u,k,*v = x
    for y in v:
        graph[u].append(y)
seen = [0]*(n+1) #1_indexed
stack = []
time = 0
seentime = [0]*(n+1) #1_indexed
donetime = [0]*(n+1) #1_indexed
def dfs(j):
    global time
    seen[j] = 1
    stack.append(j)
    time += 1
    seentime[j] = time
    while stack:
        s = stack[-1]
        if not graph[s]:
            stack.pop()
            time += 1
            donetime[s] = time
            continue
        g_NO = graph[s].popleft()
        if seen[g_NO]:
            continue
        seen[g_NO] = 1
        stack.append(g_NO)
        time += 1
        seentime[g_NO] = time
for j in range(1,n+1):
    if seen[j]:
        continue
    dfs(j)
for k,a,b in zip(range(1,n+1),seentime[1:],donetime[1:]):
    print(k,a,b)

Python: def dfs(v, u=0):
    global n, t, d, f, reached
    if len(reached) == n:
        return

    d[u] = t
    t += 1

    reached.add(u + 1)
    for ui in range(v[u][1]):
        next = v[u][2 + ui]
        if next in reached:
            continue
        dfs(v, next - 1)

    f[u] = t
    t += 1


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

t = 1
d = [0] * n
f = [0] * n
reached = set()


for ui in range(n):
    if ui + 1 not in reached:
        dfs(v, ui)

for ui in range(n):
    print('%d %d %d' % (ui + 1, d[ui], f[ui]))

Python: import sys

n = int(input())
G = [None]*(n+1)
for i in range(n):
    u, k, *vs = map(int, input().split())
    G[u] = vs

d = [-1]*(n+1)
f = [-1]*(n+1)
lb = iter(range(1, 2*n+1)).__next__
def dfs(v):
    d[v] = lb()
    for w in G[v]:
        if d[w] != -1: #already visited
            continue
        dfs(w)
    f[v] = lb()

for v in range(1, n+1):
    if d[v] != -1:
        continue
    dfs(v)

for v in range(1, n+1):
    print(f"{v} {d[v]} {f[v]}")

Python: 
d = None
f = None


def dfs(g, cur, time):
    global d, f
    d[cur] = time
    for next in g[cur]:
        if d[next] == 0:
            time = dfs(g, next, time+1)
    f[cur] = time + 1
    return time + 1


def main():
    global d, f
    n = int(input())
    g = [[] for _ in range(n)]
    d = [0 for _ in range(n)]
    f = [0 for _ in range(n)]
    for _ in range(n):
        inp = list(map(int, input().split()))
        m = inp[0] - 1
        k = inp[1]
        for i in range(k):
            g[m].append(inp[i+2] - 1)
    time = 1
    for i in range(n):
        if d[i] == 0:
            time = dfs(g, i, time) + 1
    for i in range(n):
        print(i+1, d[i], f[i])


if __name__ == '__main__':
    main()

Python: n = int(input())
trees = [[]]
for i in range(n):
    edges = []
    s = list(map(int, input().split()))
    num = s[1]
    if s[1] != 0:
        edges = s[2:2+num]
    trees.append(edges)

d = [0] * (n+1)
f = [0] * (n+1)


def dfs(x, i=0):
    for y in trees[x]:
        if d[y] != 0:
            continue
        d[y] = i
        i += 1
        i = dfs(y, i)
    f[x] = i
    i += 1
    return i


i = 1
for j in range(1, n+1):
    if d[j] != 0:
        continue
    else:
        d[j] = i
        i += 1
    i = dfs(j, i)
for i in range(1, n+1):
    print(i, d[i], f[i])

Python: def dfs(v, cnt):
    D[v] = cnt
    cnt += 1
    for c in edge[v]:
        if D[c] == -1:
            cnt = dfs(c, cnt)
    F[v] = cnt
    cnt += 1
    return cnt


V = int(input())
edge = [[] for _ in range(V)]
for _ in range(V):
    u, _, *v = map(lambda x: int(x)-1, input().split())
    edge[u] = sorted(v)
D, F = [-1] * V, [-1] * V
c = 1
for i in range(V):
    if D[i] == -1:
        c = dfs(i, c)
for i, (d, f) in enumerate(zip(D, F)):
    print(i, d, f)

Python: import sys
input = sys.stdin.readline
d = []
f = []
time = 0
def search(r):
    global d,f,time,s
    if d[r] == 0:
        time += 1
        d[r] = time
    else:
        return
    for x in s[r]:
        search(x)
    time += 1
    f[r] = time


n = int(input())
s = [[] for i in range(n+1)]
for _ in range(n):
    d = list(map(int,input().split()))
    s[d[0]] = d[2:]

d = [0 for _ in range(n+1)]
f = [0 for _ in range(n+1)]

for i in range(1,n+1):
    if d[i] == 0:
        search(i)

for i in range(1,n+1):
    print (i,d[i],f[i])

Python: n = int(input()) # 点の総数
path = list() # path[p] : 点pから行ける点のリスト
path.append([])
for _ in range(n):
    tmp = list(map(int, input().split())) # tmp[0]から行ける点はtmp[1]個あり、それはtmp[2:]
    path.append(tmp[2:])

d = [0]*(n+1) # 最初に発見した時刻
f = [0]*(n+1) # 隣接リストを調べ終えた時刻

TIME = 0

def dfs(p, d, f):
    global TIME
    TIME += 1
    d[p] = TIME # 来訪時刻の記録

    for nxt in path[p]: #繋がってる点の内
        if d[nxt] == 0: # 未探索の場合には
            dfs(nxt, d, f) # 先に進む
    
    TIME += 1
    f[p] = TIME # 繋がってる全ての点を探索し終えたらその点でやることは終わり

    
    return     
    
for start in range(1, n+1):
    if d[start] == 0:
        dfs(start, d, f)

for i in range(1, n+1):
    print(i, d[i], f[i])