Go: package main

/*
深さ優先探索
*/

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

func main() {
	scanner := makeScanner(8192)
	n := eGetInt(scanner)
	g := make([][]int, n+1)
	for i := 1; i < n+1; i++ {
		ints := eGetInts(scanner)
		num, vcount := ints[0], ints[1]
		g[i] = make([]int, vcount)
		for j := 0; j < vcount; j++ {
			g[num][j] = ints[j+2]
		}
	}
	hist := make([]struct{ d, f int }, n+1)
	t := 0
	var visit func(int)
	visit = func(num int) {
		if hist[num].d == 0 {
			stack := []int{}
			t++
			hist[num].d = t
			stack = append(stack, num)
			for len(stack) > 0 {
				// トップを訪問する
				top := stack[len(stack)-1]
				for i := 0; i < len(g[top]); i++ {
					if hist[g[top][i]].d == 0 {
						t++
						hist[g[top][i]].d = t
						stack = append(stack, g[top][i])
						goto endloop
					}
				}
				// 訪問場所がなくなったら
				// pop
				stack = stack[0 : len(stack)-1]
				t++
				hist[top].f = t
			endloop:
			}
		}
	}
	for i := 1; i < n+1; i++ {
		visit(i)
	}
	for i := 1; i < n+1; i++ {
		fmt.Println(i, hist[i].d, hist[i].f)
	}
}

////////////////////////////////////////////////////////////////////////////////
// ライブラリ
////////////////////////////////////////////////////////////////////////////////
func makeScanner(maxByte int) *bufio.Scanner {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Buffer(make([]uint8, 0, 8192), maxByte)
	return scanner
}
func makeWordScanner() *bufio.Scanner {
	scanner := makeScanner(8192)
	scanner.Split(bufio.ScanWords)
	return scanner
}
func eGetLine(r *bufio.Scanner) string {
	if r.Scan() {
		return r.Text()
	}
	err := r.Err()
	if err != nil {
		log.Fatal(err)
	}
	// EOF
	return ""
}
func eGetInt(r *bufio.Scanner) int {
	line := eGetLine(r)
	return eAtoi(line)
}
func eGetInt64(r *bufio.Scanner) int64 {
	line := eGetLine(r)
	return eAtoInt64(line)
}
func eGetFields(r *bufio.Scanner) []string {
	line := eGetLine(r)
	return strings.Fields(line)
}
func eGetInts(r *bufio.Scanner) []int {
	fields := eGetFields(r)
	ints := make([]int, len(fields))
	for i := 0; i < len(ints); i++ {
		ints[i] = eAtoi(fields[i])
	}
	return ints
}
func eGetInt64s(r *bufio.Scanner) []int64 {
	fields := eGetFields(r)
	ints := make([]int64, len(fields))
	for i := 0; i < len(ints); i++ {
		ints[i] = eAtoInt64(fields[i])
	}
	return ints
}
func eGetChars(r *bufio.Scanner) []string {
	line := eGetLine(r)
	return strings.Split(line, "")
}
func eAtoi(s string) int {
	n, err := strconv.Atoi(s)
	if err != nil {
		log.Fatal(err)
	}
	return n
}
func eAtoInt64(s string) int64 {
	n, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		log.Fatal(err)
	}
	return n
}

Python: n=int(input())
adj=[]
for i in range(n):
	a=list(map(int,input().split()))
	for j in range(2,len(a)):
		a[j]-=1
	adj.append(a[2:])
visit_time1=[0]*n
visit_time2=[0]*n
visited=[0]*n
def dfs(now):
	global time
	visit_time1[now]=time
	time+=1
	for next in adj[now]:
		if not visited[next]:
			visited[next]=True
			dfs(next)
	visit_time2[now]=time
	time+=1
time=1
for i in range(n):
	if not visited[i]:
		visited[i]=True
		dfs(i)
for i in range(n):
	print(i+1,visit_time1[i],visit_time2[i])

Python: n = int(input())

A = [[0] * n for _ in range(n)]

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

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

t = 0
d = [None] * n
f = [None] * n

def dfs_recursive(u):
    global t
    t += 1
    d[u] = t
    for v in range(n):
        if A[u][v] == 1 and d[v] is None:
            dfs_recursive(v)
    t += 1
    f[u] = t

def dfs_stack(root):
    global t
    stack = []
    stack.append(root)
    while stack:

        # 一番最後の要素をなめる
        u = stack[-1]

        # 未訪問の場合は訪問済みにする
        if d[u] is None:
            t += 1
            d[u] = t

        # 次の訪問先を探す
        has_next_node = False
        for v in range(n):
            if A[u][v] == 1 and d[v] is None:
                stack.append(v)
                has_next_node = True
                break

        # 次の訪問先がなければ訪問終了
        if not has_next_node:
            t += 1
            # スタックから取り出す
            stack.pop()

            # 時刻を記録
            f[u] = t
            
for i in range(n):
    if d[i] is None:
        dfs_recursive(i)
        # dfs_stack(i)

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

Python: N = input()
color = [0] * N
d = [0] * N
f = [0] * N
nt = [0] * N
m = [[0] * N for i in range(N)]
S = []
tt = 0

def next(u):
    global nt
    w = nt[u]
    for v in range(w, N):
        nt[u] = v + 1
        if( m[u][v] ):
            return v
    return -1

def dfs_visit(r):
    global tt 
    S.append(r)
    color[r] = 1
    tt += 1
    d[r] = tt

    while( len(S) != 0 ):
        u = S[-1]

        v = next(u)
        if ( v != -1 ):
            if ( color[v] == 0 ):
                color[v] = 1
                tt += 1
                d[v] = tt
                S.append(v)
        else:
            S.pop()
            color[u] = 2
            tt += 1
            f[u] = tt
        
def dfs():
    global color
    for u in range(N):
        if( color[u] == 0 ):
            dfs_visit(u)

if __name__ == '__main__':

    for i in range(N):
        v = map(int, raw_input().split())
        for j in v[2:]:
            m[i][j-1] = 1
    
    dfs()

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

Python: class Node:

    def __init__(self):
        self.distime = 0
        self.fintime = 0

    def input_data(self, nodes):
        data = input().split()
        self.number = int(data[0])
        self.v = [nodes[int(x) - 1] for x in data[2:]]

    @property
    def status(self):
        return bool(self.distime) + bool(self.fintime)

class Stack:
    def __init__(self, size):
        self.size = size
        self.N = [None for _ in range(size)]
        self.top = 0

    def is_emtpy(self):
        return self.top == 0

    def is_full(self):
        return self.top == self.size

    def front(self):
        return self.N[self.top - 1]

    def push(self, x):
        if self.is_full():
            raise OverflowError
        self.N[self.top] = x
        self.top += 1

    def pop(self):
        if self.is_emtpy():
            raise IndexError
        self.top -= 1
        return self.N[self.top]


def visit(node: Node):
    global t
    stack.push(node)
    t += 1
    node.distime = t
    while not stack.is_emtpy():
        u: Node = stack.front()
        try:
            v = next(i for i in u.v if i.status == 0)
        except StopIteration:
            stack.pop()
            t += 1
            u.fintime = t
        else:
            if v.status == 0:
                stack.push(v)
                t += 1
                v.distime = t


n = int(input())
stack = Stack(n)
nodes = [Node() for _ in range(n)]
[node.input_data(nodes) for node in nodes]
t = 0
for node in nodes:
    if node.status == 0:
        visit(node)

nodes.sort(key=lambda i: i.number)
for node in nodes:
    print(f"{node.number} {node.distime} {node.fintime}")

Python: # https://onlinejudge.u-aizu.ac.jp/courses/lesson/1/ALDS1/11/ALDS1_11_B
# 本の解説通りかは知らない

# データの読み込み
N = int(input())
DG = {}
node_ls = []
is_visited = {}
# 有向グラフをディクトで管理することにする
for _ in range(N):
    tmp = input().split()
    node_id = tmp[0]
    node_ls.append(node_id)
    is_visited[node_id] = False
    adj_num = int(tmp[1])
    if adj_num != 0:
        DG[node_id] = tmp[2:]
    else:
        DG[node_id] = []

d = {}  # 最初に訪問したときの発見時刻を記録する
f = {}  # 隣接リストを調べ終えた完了時刻を記録する
t = 1


def dfs(node):  # 必要なものは何？ 現在のノード
    # 終了条件
    global t
    if is_visited[node]:
        return  # 何もせずに上の関数に抜ける

    is_visited[node] = True
    # 発見時刻の書き込み
    d[node] = t
    t += 1  # 時刻を一つ進める
    # 再帰探索
    for no in DG[node]:
        dfs(no)
    f[node] = t
    t += 1  # 関数を終了するときにも+1する


for node in node_ls:  # 孤立しているノードもあるためこうして対処
    dfs(node)

for node in node_ls:
    print(node, d[node], f[node])