Go: package main

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

// I/O
type Scanner struct {
	sc *bufio.Scanner
}

func NewScanner() *Scanner {
	sc := bufio.NewScanner(os.Stdin)
	sc.Split(bufio.ScanWords)
	sc.Buffer(make([]byte, 1024), int(1e+9))
	return &Scanner{sc}
}

func (s *Scanner) nextStr() string {
	s.sc.Scan()
	return s.sc.Text()
}

func (s *Scanner) nextInt() int {
	i, e := strconv.Atoi(s.nextStr())
	if e != nil {
		panic(e)
	}
	return i
}

func (s *Scanner) nextFloat() float64 {
	f, e := strconv.ParseFloat(s.nextStr(), 64)
	if e != nil {
		panic(e)
	}
	return f
}

func (s *Scanner) nextRuneSlice() []rune {
	return []rune(s.nextStr())
}

func (s *Scanner) nextIntSlice(n int) []int {
	res := make([]int, n)
	for i := 0; i < n; i++ {
		res[i] = s.nextInt()
	}
	return res
}

func (s *Scanner) nextFloatSlice(n int) []float64 {
	res := make([]float64, n)
	for i := 0; i < n; i++ {
		res[i] = s.nextFloat()
	}
	return res
}

// Arithmetic
func max(nums ...int) int {
	m := nums[0]
	for _, i := range nums {
		if m < i {
			m = i
		}
	}
	return m
}

func min(nums ...int) int {
	m := nums[0]
	for _, i := range nums {
		if m > i {
			m = i
		}
	}
	return m
}

func abs(x int) int {
	if x > 0 {
		return x
	}
	return -x
}

func pow(x, y int) int {
	res := 1
	for i := 0; i < y; i++ {
		res *= x
	}
	return res
}

func ceil(a, b int) int {
	if a%b == 0 {
		return a / b
	} else {
		return a/b + 1
	}
}

// Sort
type RuneSlice []rune

func (a RuneSlice) Len() int           { return len(a) }
func (a RuneSlice) Less(i, j int) bool { return a[i] < a[j] }
func (a RuneSlice) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

// Main
func main() {
	sc := NewScanner()
	wtr := bufio.NewWriter(os.Stdout)
	N := sc.nextInt()
	G := make([][]int, N)
	for i := 0; i < N; i++ {
		u, k := sc.nextInt()-1, sc.nextInt()
		for j := 0; j < k; j++ {
			G[u] = append(G[u], sc.nextInt()-1)
		}
	}
	ans := make([][2]int, N)
	time := 0
	found := make(map[int]bool)
	var dfs func(cur int)
	dfs = func(cur int) {
		time++
		ans[cur][0] = time
		found[cur] = true
		for i := 0; i < len(G[cur]); i++ {
			next := G[cur][i]
			if !found[next] {
				dfs(G[cur][i])
			}
		}
		time++
		ans[cur][1] = time
	}
	for i := 0; i < N; i++ {
		if !found[i] {
			dfs(i)
		}
	}

	for i := 0; i < N; i++ {
		fmt.Fprint(wtr, i+1)
		for j := 0; j < 2; j++ {
			fmt.Fprint(wtr, " ")
			fmt.Fprint(wtr, ans[i][j])
		}
		fmt.Fprintln(wtr)
	}
	wtr.Flush()
}

Python: n = int(input())
A = [[False for _ in range(n)] for _ in range(n)]
for _ in range(n):
    tmp = list(map(int, input().split()))
    for i in range(tmp[1]):
        A[tmp[0]-1][tmp[i+2]-1] = True

WHITE = 0
GRAY = 1
BLACK = 2
color = [WHITE for _ in range(n)]
d = [0 for _ in range(n)]
f = [0 for _ in range(n)]
time = 1

def dfs(u):
    global dfs_color, d, f, time
    color[u] = GRAY
    d[u] += time
    time += 1
    for v in range(n):
        if A[u][v]:
            if color[v] == WHITE:
                dfs(v)
        else:
            continue
    color[u] = BLACK
    f[u] = time
    time += 1 

for u in range(n):
    if color[u] == WHITE:
        dfs(u)

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

Python: n = int(input())
graph = {}
for _ in range(n):
  i = list(map(int,input().split()))
  graph[i[0]] = i[2:]

first = {i:0 for i in range(1,n+1)}
last = {i:0 for i in range(1,n+1)}
seen = set()
time = 1

def dfs(v):
  global time
  seen.add(v)
  first[v] = time
  time += 1
  for nxt in graph[v]:
    if nxt in seen:
      continue
    dfs(nxt)
  last[v] = time
  time += 1

for i in range(1,n+1):
  if i not in seen:
    dfs(i)
  
for i in range(1,n+1):
  print(i, first[i], last[i])

Python: import sys

def debug(x, table):
    for name, val in table.items():
        if x is val:
            print('DEBUG:{} -> {}'.format(name, val), file=sys.stderr)
            return None

time = 0

def dfs(u, adj, d, f, sumi):
    global time
    time += 1
    d[u] = time

    for v in adj[u]:
        if v not in sumi:
            dfs(v, adj, d, f, sumi)
        sumi.add(v)

    time += 1

    f[u] = time


def solve():
    n = int(input())
    adj = [None] * n

    for i in range(n):
        u, k, *vs = [int(j) - 1 for j in input().split()]
        # debug(vs, locals())
        adj[u] = vs

    # debug(adj, locals())
    
    d = [0] * n
    f = [0] * n
    sumi = {0}

    dfs(0, adj, d, f, sumi)

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

    pass

if __name__ == '__main__':
    solve()

Python: #!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

WHITE, GRAY, BLACK = 0, 1, 2


def generate_adj(v_info):
    for v_detail in v_info:
        v_adj_list = v_detail[2:]
        assert len(v_adj_list) == int(v_detail[1])
        for each in v_adj_list:
            init_adj_matrix[int(v_detail[0]) - 1][int(each) - 1] = 1
    return init_adj_matrix


def graph_dfs(start_node_index):
    # init the first node
    v_index = int(start_node_index)
    vertex_visit_stack.append(v_index)
    vertex_color_list[v_index] = GRAY
    time = 1
    vertex_d_time[v_index] += time
    # init end
    while len(vertex_visit_stack) > 1:
        current_vertex = vertex_visit_stack[-1]
        try:
            next_vertex = adj_matrix[current_vertex - 1].index(1) + 1
        except ValueError:
            # print('err')
            vertex_visit_stack.pop()
            vertex_color_list[current_vertex] = BLACK
            time += 1
            vertex_f_time[current_vertex] += time
        else:
            if vertex_color_list[next_vertex] == WHITE:
                # print('w2g', current_vertex, next_vertex, vertex_color_list[next_vertex])
                vertex_color_list[next_vertex] = GRAY
                time += 1
                vertex_d_time[next_vertex] += time
                vertex_visit_stack.append(next_vertex)
                # node A to B only visit once
                adj_matrix[current_vertex - 1][next_vertex - 1] = 0
            else:
                time += 1
                vertex_f_time[current_vertex] += time
                vertex_visit_stack.pop()
    return None


if __name__ == '__main__':
    _input = sys.stdin.readlines()
    vertex_num = int(_input[0])
    vertex_info = list(map(lambda x: x.split(), _input[1:]))
    init_adj_matrix = [[0] * vertex_num for _ in range(vertex_num)]
    # assert len(vertex_info) == vertex_num

    vertex_visit_stack = [-1]
    vertex_color_list = [WHITE] * (vertex_num + 1)
    vertex_d_time, vertex_f_time = [0] * (vertex_num + 1), [0] * (vertex_num + 1)
    adj_matrix = generate_adj(vertex_info)

    graph_dfs(start_node_index=1)
    for i in range(vertex_num):
        print(i + 1, vertex_d_time[i + 1], vertex_f_time[i + 1])

Python: n = int(input())

graph = []
for _ in range(n):
    tmp = list(map(int, input().split()))
    if tmp[1] == 0:
        graph.append([])
    else:
        graph.append(tmp[2:])
#print(graph)

seen = [0]*n
to_do = []
in_list = [0]*n
out_list = [0]*n
count = 0

def dfs(v):
    #print(v)
    global count
    if seen[v] == 0:
        seen[v] = 1
        count += 1
        in_list[v] = count
        to_do = graph[v]
        #print(in_list)
        #print(to_do)
        if len(to_do) > 0:
            for i in to_do:
                dfs(i-1)
        count += 1
        out_list[v] = count
        #print(out_list)
while True:
    if 0 in seen:
        k = seen.index(0)
        dfs(k)
    else:
        break

for i in range(n):
    print("{} {} {}".format(i+1,in_list[i],out_list[i]))