Go: package main

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

func getScanner(fp *os.File) *bufio.Scanner {
	scanner := bufio.NewScanner(fp)
	scanner.Split(bufio.ScanWords)
	scanner.Buffer(make([]byte, 1000005), 1000005)
	return scanner
}

func getNextString(scanner *bufio.Scanner) string {
	scanner.Scan()
	return scanner.Text()
}

func getNextInt(scanner *bufio.Scanner) int {
	i, _ := strconv.Atoi(getNextString(scanner))
	return i
}

func getNextInt64(scanner *bufio.Scanner) int64 {
	i, _ := strconv.ParseInt(getNextString(scanner), 10, 64)
	return i
}

func getNextUint64(scanner *bufio.Scanner) uint64 {
	i, _ := strconv.ParseUint(getNextString(scanner), 10, 64)
	return i
}

func getNextFloat64(scanner *bufio.Scanner) float64 {
	i, _ := strconv.ParseFloat(getNextString(scanner), 64)
	return i
}

func main() {
	fp := os.Stdin
	wfp := os.Stdout
	cnt := 0

	if os.Getenv("MASPY") == "ますピ" {
		fp, _ = os.Open(os.Getenv("BEET_THE_HARMONY_OF_PERFECT"))
		cnt = 1
	}
	if os.Getenv("MASPYPY") == "ますピッピ" {
		wfp, _ = os.Create(os.Getenv("NGTKANA_IS_GENIUS10"))
	}

	scanner := getScanner(fp)
	writer := bufio.NewWriter(wfp)
	solve(scanner, writer)
	for i := 0; i < cnt; i++ {
		fmt.Fprintln(writer, "-----------------------------------")
		solve(scanner, writer)
	}
	writer.Flush()
}

func solve(scanner *bufio.Scanner, writer *bufio.Writer) {
	n := getNextInt(scanner)
	g := graph{}
	g.ee = make([]Edges, n)
	g.visited = make([]bool, n)
	g.found = make([]int, n)
	g.leave = make([]int, n)
	for i := 0; i < n; i++ {
		u := getNextInt(scanner) - 1
		k := getNextInt(scanner)
		for j := 0; j < k; j++ {
			v := getNextInt(scanner) - 1
			g.ee[u] = append(g.ee[u], Edge{
				to: v,
				id: u,
			})
		}
	}
	for i := 0; i < n; i++ {
		sort.Sort(g.ee[i])
	}
	for i := 0; i < n; i++ {
		if g.visited[i] {
			continue
		}
		g.dfs(i)
	}

	for i := 0; i < n; i++ {
		fmt.Fprintln(writer, fmt.Sprintf("%d %d %d", i+1, g.found[i], g.leave[i]))
	}
}

// Edge ...
type Edge struct {
	to, id int
}

type Edges []Edge

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

type graph struct {
	ee           []Edges
	t            int
	n            int
	visited      []bool
	found, leave []int
}

func (g *graph) dfs(i int) {
	g.visited[i] = true
	g.t++
	g.found[i] = g.t
	for _, e := range g.ee[i] {
		if g.visited[e.to] {
			continue
		}
		g.dfs(e.to)
	}
	g.t++
	g.leave[i] = g.t
}

Python: def dfs_recursive(u):
    global time
    color[u] = 'gray'
    arrive[u] = time
    time += 1
    for v in range(n):
        if adjMat[u][v] == 1 and color[v] == 'white':
            dfs_recursive(v)
    color[u] = 'black'
    finish[u] = time
    time += 1


n = int(input())

adjMat = [[0] * n for _ in range(n)]
color = ['white'] * n
arrive = [0] * n
finish = [0] * n
time = 1

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

for root in range(n):
    if color[root] == 'white':
        dfs(root)


for i in range(n):
    out = ''
    out += '{} {} {}'.format(i + 1, arrive[i], finish[i])
    print(out)

Python: import sys
input = sys.stdin.readline
v = []
tl = []
t = 1

def rec(node):
	global t, tl, v
	if tl[node-1][0] != -1:
		# print("ret", node, tl[node-1][0])
		return
	tl[node-1][0] = t
	t += 1
	# print("t: {}", t)
	adj = v[node-1][2:]
	# print("adj: ", adj)
	for i in adj:
		rec(v[i-1][0])
	tl[node-1][1] = t
	t += 1

def main():
	global v, tl
	n = int(input().strip())
	tl = [[-1, -1] for _ in range(n)]
	for i in range(n):
		l = list(map(int, input().strip().split()))
		v.append(l)
	for i in range(1, n+1):
		rec(i)
	for i in range(n):
		print("{} {} {}".format(i+1, tl[i][0], tl[i][1]))

if __name__ == '__main__':
	main()

Python: 
def dfs_visit(u):
    global n, M, d, f, time

    if color[u] == 'WHITE':
        color[u] = 'GRAY'
        time += 1
        d[u] = time

        for v in range(n):
            if M[u][v] == 0:
                continue
            if color[v] == 'WHITE':
                dfs_visit(v)

        color[u] = 'BLACK'
        time += 1
        f[u] = time


def dfs():
    global n, M, d, f, time

    for u in range(n):
        if color[u] == 'WHITE':  # 連結グラフとは限らないため。
            dfs_visit(u)

    for u in range(n):
        print('{} {} {}'.format(u+1, d[u], f[u]))


n = int(input())

M = [[0 for i in range(n)] for j in range(n)]
color = ['WHITE'] * n  # 各頂点の色を記録。ホワイト（未探索）、グレイ（探索中）、ブラック（探索ずみ）
d = [None] * n  # vを最初に発見した時間
f = [None] * n  # vの完了時刻
time = 0

for i in range(n):
    _ = list(map(int, input().split()))
    u = _[0] - 1  # 0オリジンに変換

    degree = _[1]
    for j in range(degree):
        v = _[j+2] - 1  # 0オリジンに変換
        M[u][v] = 1

# for row in M:
#     print(' '.join(map(str, row)))

dfs()

Python: n = int(input())
A = [[0 for i in range(n)] for j in range(n)]

for i in range(n):
    u, k, *v = list(map(int, input().split()))
    for j in v:
        A[int(u)-1][int(j)-1] = 1
        # A[int(j)-1][int(u)-1] = 1

# for row in A:
#     msg = ' '.join(map(str, row))
#     # print(msg)

d = [-1] * n
f = [-1] * n

t = 0
d[0] = t

# recursive


def dfs(u):
    global t
    # print('visit:', str(u), str(t))
    t += 1
    d[u] = t
    for i in range(len(A[u])):
        if A[u][i] == 1 and d[i] == -1:
            dfs(i)
    t += 1
    f[u] = t
            


for i in range(n):
    if d[i-1] == -1:
        dfs(i-1)


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

Python: n = int(input())
d = [0]*n
f = [0]*n
G = [[] for _ in range(n)]
for _ in range(n):
    l = list(map(int, input().split()))
    u, k = l[:2]
    l = list(map(lambda x: x-1, l[2:]))
    G[u-1] = l

timestamp = [i+1 for i in range(2*n)]
def dfs(node):
    global d,f,G,timestamp
    if d[node]>0:
        return False
    d[node] = timestamp.pop(0)
    for v in G[node]:
        dfs(v)
    if f[node]==0:
        f[node] = timestamp.pop(0)
    return True
for i in range(n):
    dfs(i)
    
for i in range(n):
    print(i+1, d[i], f[i])

Python: def dfs(vum,v,d,f,t):
    # print('first')
    # print(vum,v,d,f,t)
    if f[v] >= 0:
        return d,f,t
    t += 1
    d[v] = t
    for u in vum[v]:
        # print(v, d[v], f[v], u, d[u], f[u])
        if d[u] >= 0:
            # print('skip', str(v), str(u))
            continue
        d,f,t = dfs(vum,u,d,f,t)
    t += 1
    f[v] = t
    # print(v, d[v], f[v])
    # print('last')
    # print(vum,v,d,f,t)
    return d,f,t

def main():
    n = int(input())
    rows = []
    for i in range(n):
        rows.append([int(x) for x in input().split()])

    vum = {}
    for v,row in enumerate(rows):
        u,k = row[0],row[1]
        ul = []
        if k > 0:
            for j in row[2:]:
                ul.append(j-1)
        vum.update({v:ul})

    # print(vum)
    d = [-1]*len(vum)
    f = [-1]*len(vum)
    t = 0
    for v in vum:
        d,f,t = dfs(vum,v,d,f,t)
    for i,(j,k) in enumerate(zip(d,f)):
        print(i+1,j,k)

if __name__ == '__main__':
    main()

Python: n=int(input())
adj=[None]
for i in range(n):
    adj_i=list(map(int,input().split()[2:]))
    adj.append(adj_i)
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:])