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: # -*- coding: utf-8 -*-
"""
Created on Sun Jul  8 10:56:33 2018

@author: maezawa
"""
n = int(input())
adj = [[] for _ in range(n)]
d = [0 for _ in range(n)]
f = [0 for _ in range(n)]

for j in range(n):
    ain = list(map(int, input().split()))
    u = ain[0]
    k = ain[1]
    for i in range(k):
        adj[u-1].append(ain[i+2]-1)
    adj[u-1].sort()
    #print(*adj[u-1])

t = 1

for j in range(n):
    if d[j] == 0:
        stack = [j]

    while stack:
        #print(stack)
        current = stack[-1]
        if d[current] == 0:
            d[current] = t
            t += 1
        flag = 0
        for k in adj[current]:
            if d[k] == 0:
                stack.append(k)
                flag = 1
                break
        if flag == 0:
            f[current] = t
            t += 1
            stack.pop()

    #print('current = {}, t = {}'.format(current, t))
    #print('stack', *stack)

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

Python: def get_unvisted_child(v, timestamp):
  while len(G[v]) > 0:
    c = G[v].pop(0)
    if not c in timestamp: return c
  return -1

def get_unvisited_node(G, timestamp):
  for v in sorted(G):
    if not v in timestamp: return v
  return -1

def dept_first_search(G):
  t = 1
  timestamp = {}
  stack = []

  v = get_unvisited_node(G, timestamp)
  while v > 0:
    stack.append(v)
    timestamp[v] = [t]
    t += 1

    while len(stack) > 0:
      v = stack[-1]
      c = get_unvisted_child(v, timestamp)
      if c > 0:
        #c = G[v].pop(0)
        stack.append(c)
        timestamp[c] = [t]
      else:
        timestamp[v].append(t)
        stack.pop()
      t += 1

    v = get_unvisited_node(G, timestamp)

  for v in sorted(timestamp):
    print(v, *timestamp[v])

G = {}
for i in range(int(input())):
  x = list(map(int, input().split()))
  G[x[0]] = x[2:]

dept_first_search(G)

Python: def d(u):
 global t
 C[u]=1;D[u]=t;t+=1
 for v in M[u]:v=int(v)-1;C[v]or d(v)
 F[u]=t;t+=1
N=int(input());R=range(N)
M=[input().split()[2:]for _ in R]
C,D,F=[0]*N,[0]*N,[0]*N
t=1
for i in R:C[i]or d(i)
for i in R:print(i+1,D[i],F[i])

Python: #!/usr/bin/env python3

import sys
import math
from bisect import bisect_right as br
from bisect import bisect_left as bl
sys.setrecursionlimit(2147483647)
from heapq import heappush, heappop,heappushpop
from collections import defaultdict
from itertools import accumulate
from collections import Counter
from collections import deque
from operator import itemgetter
from itertools import permutations
mod = 10**9 + 7
inf = float('inf')
def I(): return int(sys.stdin.readline())
def LI(): return list(map(int,sys.stdin.readline().split()))

cnt = 1
def dfs(x):
    global cnt
    if s[x] == None:
        s[x] = cnt
        cnt += 1
    for v in edges[x]:
        if s[v] == None:
            dfs(v)
    g[x] = cnt
    cnt += 1
    return 

n = I()
edges = [[] for _ in range(n)]
for i in range(n):
    edges[i] = list(map(lambda x:x-1,LI()[2:]))

s = [None]*n
g = [None]*n
for i in range(n):
    if s[i] == None:
        dfs(i)
for i in range(n):
    print(i+1,s[i],g[i])

Python: from typing import List


def dfs(adj_matrix: List[List[int]], vartex: int) -> int:
    global step
    global discover_times
    global finish_times
    if 0 != discover_times[vartex]:
        return 0
    discover_times[vartex] = step

    for col_idx in range(len(adj_matrix)):
        if (0 != adj_matrix[vartex][col_idx]) and (0 == discover_times[col_idx]):
            step += 1
            dfs(adj_matrix, col_idx)

    step += 1
    finish_times[vartex] = step

    return 1


if __name__ == "__main__":
    num_vertices = int(input())
    adj_matrix = [[0] * num_vertices for i in range(num_vertices)]

    for _ in range(num_vertices):
        vertex, _, *adjs = map(lambda x: int(x), input().split())
        for adj in adjs:
            adj_matrix[vertex - 1][adj - 1] = 1

    step = 1
    discover_times = [0] * num_vertices
    finish_times = [0] * num_vertices

    for vartex in range(num_vertices):
        if 1 == dfs(adj_matrix, vartex):
            step += 1

    for vertex, (d, f) in enumerate(zip(discover_times, finish_times)):
        print(f"{vertex + 1} {d} {f}")

Python: def dfs(u, node_info, matrix):
    # ????????????????????¨??????????????????
    colors = [0] * len(matrix)
    detect_time = [0] * len(matrix)
    finished_time = [0] * len(matrix)
    time = 1
    stack = list()
    stack.append(u)
    colors[u] = 1
    detect_time[u] = time
    while len(stack):
        for i, val in enumerate(matrix[u]):
            if val == 1 and colors[i] == 0:
                time += 1
                stack.append(i)
                colors[i] = 1
                detect_time[i] = time
                u = i
                break
        else:
            time += 1
            e = stack.pop()
            colors[e] = 2
            finished_time[e] = time
            if len(stack):
                u = stack[-1]
    for i in range(len(matrix)):
        print('{} {} {}'.format(i + 1, detect_time[i], finished_time[i]))


if __name__ == '__main__':
    N = int(input())
    matrix = [[0] * N for _ in range(N)]
    color = [0] * N
    for i in matrix:
        node_info = [int(i) for i in input().split()]
        node_i = node_info[0] - 1
        if not node_info[1] == 0:
            for i in node_info[2:]:
                matrix[node_i][i - 1] = 1
    dfs(0, None, matrix)