Go: package main

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

func main() {
	scn := bufio.NewScanner(os.Stdin)
	scn.Split(bufio.ScanWords)
	nextInt := func() int { scn.Scan(); r, _ := strconv.Atoi(scn.Text()); return r }
	w := bufio.NewWriter(os.Stdout)

	n := nextInt()
	vecs := make([][]int, n)
	for i := 0; i < n; i++ {
		u := nextInt()
		k := nextInt()
		for j := 0; j < k; j++ {
			vecs[u-1] = append(vecs[u-1], nextInt()-1)
		}
	}

	ds := make([]int, n)
	fs := make([]int, n)
	founds := make([]bool, n)
	t := 0
	for i := range founds {
		if !founds[i] {
			t = dfs(vecs, ds, fs, founds, i, t)
		}
	}

	for i := 0; i < n; i++ {
		fmt.Fprintln(w, i+1, ds[i], fs[i])
	}

	w.Flush()
}

func dfs(vecs [][]int, ds, fs []int, founds []bool, i, t int) (time int) {
	t++
	ds[i] = t
	founds[i] = true
	for _, v := range vecs[i] {
		if !founds[v] {
			t = dfs(vecs, ds, fs, founds, v, t)
		}
	}
	t++
	fs[i] = t

	return t
}

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

import sys

WHITE = 0
GRAY = 1
BLACK = 2


def dbg_record(records):
    for index, record in enumerate(records):
        print("???????????????: %d" % index)
        if isinstance(record, dict):
            print("??\??????: %d" % record.get("visit", -1))
            print("?????????: %d" % record.get("leave", -1))
        else:
            print("?????????")
        print("")


def dfs(matrix):
    def internal_dfs(node_id, current_time):
        if state[node_id] == WHITE:
            # ?¨??????????
            state[node_id] = GRAY
            stack.append(node_id)
            records[node_id] = {"visit": current_time}  # ?¨??????????????¨????

            # ?¬?????¨????????????¨?????§????????????????????¢???
            next_id = None
            for index in range(len(matrix)):
                if matrix[node_id][index] == 1 and state[index] == WHITE:
                    next_id = index
                    break

            if next_id is not None:
                internal_dfs(next_id, current_time + 1)
            elif 0 < len(stack):
                # ?¬?????¨????????????¨?????§?????????????????????????????????????????°?????????
                internal_dfs(stack.pop(), current_time + 1)
        elif state[node_id] == GRAY:
            # ????????????

            # ?¬?????¨????????????¨?????§????????????????????¢???
            next_id = None
            for index in range(len(matrix)):
                if matrix[node_id][index] == 1 and state[index] == WHITE:
                    next_id = index
                    break

            if next_id is not None:
                stack.append(node_id)
                # internal_dfs(next_id, current_time + 1)
                internal_dfs(next_id, current_time)
            elif 0 < len(stack):
                # ?¬?????¨????????????¨?????§?????????????????????????????????????????°?????????
                state[node_id] = BLACK
                records[node_id]["leave"] = current_time  # ????????????????¨????
                internal_dfs(stack.pop(), current_time + 1)
            else:  # ??????????§????????????£???????????¨??? (node_id should be 0)
                state[node_id] = BLACK
                records[node_id]["leave"] = current_time
        elif state[node_id] == BLACK:
            print("Black!!")

    state = [WHITE] * len(matrix)  # White, Gray, Black
    records = [None] * len(matrix)  # Save time when the node is processed
    stack = []

    internal_dfs(0, 1)
    # dbg_record(records)
    return records


if __name__ == "__main__":
    # ??????????????????
    lines = [
        "6",
        "1 2 2 3",
        "2 2 3 4",
        "3 1 5",
        "4 1 6",
        "5 1 6",
        "6 0",
    ]
    # lines = sys.stdin.readlines()

    # Create n * n matrix
    dimension = int(lines[0])
    matrix = []
    for x in range(dimension):
        matrix.append([0] * dimension)

    # Set edge info
    for index, line in enumerate(lines[1:]):
        for edge in [int(x) for x in line.strip().split(" ")[2:]]:
            matrix[index][edge - 1] = 1

    result = dfs(matrix)
    for index, line in enumerate(result):
        print("%d %d %d" % (index + 1, line.get("visit", -1), line.get("leave", -1)))

Python: from sys import stdin
input = stdin.readline

def DFS(node):
    global time
    if discoveredtime[node] == -1:
        discoveredtime[node] = time
        time += 1

        for child in childlist[node]:
            DFS(child)
        
        finishedtime[node] = time
        time += 1

N = int(input())

childlist = [None]*(N+1)
for _ in range(N):
    u, k, *v = map(int, input().split())
    childlist[u] = v

discoveredtime = [-1]*(N+1)
finishedtime = [-1]*(N+1)
time = 1

for i in range(1, N+1):
    if discoveredtime[i] == -1:
        DFS(i)

for i in range(1, N+1):
    print(i, discoveredtime[i], finishedtime[i])

Python: n = int(input())

adj = [ [] for _ in range (n) ]

for i in range(n):
    _, _, *adj[i] = list(map(int, input().split()))

d = [0] * n
f = [0] * n
flag = [0] * n
time = 0

def dfs(u):
    global time
    time += 1
    flag[u-1] = 1
    d[u-1] = time
    
    for v in adj[u-1]:
        if flag[v-1] == 0:
            dfs(v)

    time += 1
    f[u-1] = time

for i in range(n):
    if flag[i] == 0:
        dfs(i+1)

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

Python: n = int(input())

adj = [None]
for i in range(n):
    adj_i = list(map(int, input().split()[2:]))
    adj.append(adj_i)

# searched or not, number of vertex, discovery time and finishing time.
sndf = [None]
for i in range(1, n + 1):
    sndf.append([False, i])

path = []

time = 0

def dfs(u):
    global time
    sndf[u][0] = True
    time += 1
    sndf[u].append(time)

    for v in adj[u]:
        if not sndf[v][0]:
            dfs(v)
    time += 1
    sndf[u].append(time)


for i in range(1, n + 1):
    if not sndf[i][0]:
        dfs(i)

for x in sndf[1:]:
    print(*x[1:])