Go: package main

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

type TimeRecord struct {
	discoverTime int
	finishTime   int
}

func main() {
	sc := bufio.NewScanner(os.Stdin)
	sc.Split(bufio.ScanWords)

	n, err := nextInt(sc)
	if err != nil {
		panic(err)
	}

	timeRecordMap := make([]TimeRecord, n)
	childrenMap := make([][]int, n)
	for i := 1; i <= n; i++ {
		node, err := nextInt(sc)
		if err != nil {
			panic(err)
		}
		timeRecordMap[node-1] = TimeRecord{-1, -1}

		childNum, err := nextInt(sc)
		if err != nil {
			panic(err)
		}
		childrenMap[node-1] = make([]int, childNum)
		for j := 0; j < childNum; j++ {
			child, err := nextInt(sc)
			if err != nil {
				panic(err)
			}
			childrenMap[node-1][j] = child
		}
	}

	// TODO: always start with 1?
	timer := 1
	visit(1, &timer, childrenMap, timeRecordMap)
	for k, v := range timeRecordMap {
		fmt.Printf("%d %d %d\n", k+1, v.discoverTime, v.finishTime)
	}
}

func visit(currentNode int, timer *int, childrenMap [][]int, timeRecordMap []TimeRecord) {
	if timeRecordMap[currentNode-1].discoverTime > 0 {
		return
	}
	children := childrenMap[currentNode-1]
	timeRecordMap[currentNode-1] = TimeRecord{*timer, timeRecordMap[currentNode-1].finishTime}
	(*timer)++
	for i := 0; i < len(children); i++ {
		visit(children[i], timer, childrenMap, timeRecordMap)
	}
	timeRecordMap[currentNode-1] = TimeRecord{timeRecordMap[currentNode-1].discoverTime, *timer}
	(*timer)++
}

func nextInt(sc *bufio.Scanner) (int, error) {
	sc.Scan()
	return strconv.Atoi(sc.Text())
}

Python: class Vertex:
    _id = -1
    _children = []
    _visited = False
    _visited_time = 0
    _all_visited_time = 0

    def __init__(self, i, c):
        self._id = i
        self._children = c

    def getId(self):
        return self._id

    def getChildren(self):
        return self._children

    def isVisited(self):
        return self._visited

    def getVisitedTime(self):
        return self._visited_time

    def setVisitedTime(self, t):
        self._visited_time = t
        self._visited = True

    def getAllVisitedTime(self):
        return self._all_visited_time

    def setAllVisitedTime(self, t):
        self._all_visited_time = t

    def print(self):
        print("{} {} {}".format(self._id, self._visited_time, self._all_visited_time))


n = int(input())
graph = [0 for i in range(n)]
time = 0

# 頂点登録
for i in range(n):
    u = list(map(int, input().split(' ')))
    adj = []
    if u[1] > 0:
        adj = u[2:u[1]+2]
    graph[u[0]-1] = Vertex(u[0], adj)

# DFS
def dfs(v):
    global time
    time += 1
    v.setVisitedTime(time)
    for child in v.getChildren():
        if not graph[child-1].isVisited():
            dfs(graph[child-1])
    time += 1
    v.setAllVisitedTime(time)

# DFS実行
for v in graph:
    if not v.isVisited():
        dfs(v)

# 回答を出力
for v in graph:
    v.print()

Python: from collections import deque


def conv_list(n):
    adj_matrix = []
    for _ in range(n):
        adj_list = input().split()
        adj_list = list(map(int, adj_list))
        adj_row = [False] * n
        if adj_list[1] > 0:
            for i in adj_list[2:]:
                adj_row[i - 1] = True
        adj_matrix.append(adj_row)
    return adj_matrix


def depth_first_search(adj_matrix, n):
    visited = [None] * n
    count = 1
    searched = [None] * n
    start = 0
    stack = deque()
    stack.append(start)
    visited[start] = 1
    while count <= 2 * n:
        has_edge = adj_matrix[start]
        i = 0
        while i < n:
            if has_edge[i] and visited[i] is None:
                stack.append(i)
                count += 1
                visited[i] = count
                break
            i += 1
        if i < n:
            start = i
        else:
            searched_node = stack.pop()
            count += 1
            searched[searched_node] = count
            if len(stack) > 0:
                start = stack[-1]
            elif count < 2 * n:
                start = visited.index(None)
                stack.append(start)
                count += 1
                visited[start] = count
            else:
                break
    return visited, searched


def main():
    n = int(input())
    adj_matrix = conv_list(n)
    visited, searched = depth_first_search(adj_matrix, n)
    for i in range(n):
        print("{} {} {}".format(i + 1, visited[i], searched[i]))


if __name__ == "__main__":
    main()

Python: import sys
readline = sys.stdin.readline
write = sys.stdout.write

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


begin = [-1]*(N+1)
end = [-1]*(N+1)
lb = iter(range(1, 2*N+1)).__next__
def dfs(v):
    begin[v] = lb()
    for w in G[v]:
        if begin[w] != -1:
            continue
        dfs(w)
    end[v] = lb()


for v in range(1, N+1):
    if begin[v] != -1:
        continue
    dfs(v)
write("\n".join("{} {} {}".format(*args) for args in zip(range(1, N+1), begin[1:], end[1:])))
write("\n")

Python: WHITE = 0
GRAY = 1
BLACK = 2
color, d, f = [WHITE for i in range(n)]
t = 1

def visit(u):
    color[u] = GRAY
    d[u] += t
    t += 1
    for v in range(n):
        if (M[u-1][v-1] == 0):
            continue
        if (color[v] == WHITE):
            visit(v)
    color[u] = BLACK
    f[u] = t
    t += 1

for u in range(n):
    if (color[u] == WHITE):
        visit(u)
for i in range(n):
    print(str(i+1), str(d[i]), str(f[i]), sep = " ")

n = int(input())
M = [[[0] for i in range(n)] for i in range(n)]
for i in range(n):
    temp = list(map(int, input().split()))
    for i in range(temp[1]):
        M[temp[0]-1][temp[i+2]-1] = 1

Python: n = int(input())
graph = {}

for i in range(1, n + 1):
    data = input().split()
    graph[i] = [int(i) for i in data[2:]]


class dfs:
    def __init__(self, graph):
        self.g = graph
        self.visited = set()
        self.time = 0
        self.res = {}

    def dfs(self, cur):
        self.visited.add(cur)
        self.time += 1
        start = self.time
        for node in self.g[cur]:
            if node not in self.visited:
                self.dfs(node)
        self.time += 1
        end = self.time
        self.res[cur] = (start, end)


solver = dfs(graph)
for i in range(1, n + 1):
    if i not in solver.visited:
        solver.dfs(i)

for i in range(1, n + 1):
    print(i, solver.res[i][0], solver.res[i][1])

Python: N = int(input())

nodes = [tuple(map(int, input().split(' '))) for _ in range(N)]
d = [-1 for _ in range(N + 1)]
f = [-1 for _ in range(N + 1)]

clock = 0


def dfs(node):
    global clock
    if d[node] == -1:
        clock += 1
        d[node] = clock
    neighbors = sorted(nodes[node - 1][2:])

    for neighbor in neighbors:
        if d[neighbor] == -1:
            dfs(neighbor)

    if f[node] == -1:
        clock += 1
        f[node] = clock


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

Python: n = int(input())
mat = [[0] * n for _ in range(n)]
d, f = [0] * n, [0] * n
for _ in range(n):
    adj = list(map(int, input().split()))
    i = adj[0]
    v = adj[2:]
    for j in v:
        mat[i - 1][j - 1] = 1

time, current = 1, 0
stack = []
d[current] = time
while 0 in f:
    if not stack:
        stack.append(0)
    time += 1
    current = stack[-1]
    for i in range(len(mat[current])):
        if mat[current][i] and not d[i]:
            current = i
            d[i] = time
            stack.append(i)
            break
    else:
        f[current] = time
        current = stack.pop()

for l in zip(range(1, n + 1), d, f):
    print(*l)