Go: package main

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

/***************************************************************
stackによる深さ優先探索を実装する。
***************************************************************/

const (
	N     = 100
	WHITE = 0
	GRAY  = 1
	BLACK = 2
)

var (
	n            int
	Adj          [N][N]int
	statusColor  [N]int
	discoverTime [N]int
	finishTime   [N]int
	clock        int
	nt           [N]int
)

/***************************************************************
Stackの定義:
- field
	container: ノードの格納されたスライス
	sp: スタックポインタ
- method
	size(): スタックに積まれているノードの個数を返す
	top(): スタックの一番上に積まれているノードを返す
	isEmpty(): スタックが空かどうかを返す
	pop(): スタックの要素をポップし削除する
	push(): スタックに要素を積む
***************************************************************/

type Stack struct {
	container []int
	sp        int
}

func (s *Stack) size() int {
	return len(s.container)
}

func (s *Stack) top() int {
	return s.container[s.sp]
}

func (s *Stack) isEmpty() bool {
	return s.sp == 0
}

func (s *Stack) pop() int {
	_pop := func() {
		s.container = s.container[:len(s.container)-1]
	}
	defer _pop()
	if s.isEmpty() {
		panic("There is no elements in stack!")
	}
	s.sp--
	return s.container[s.sp]
}

func (s *Stack) push(n int) {
	s.sp++
	s.container[s.sp] = n
}

func next(u int) int {
	for v := nt[u]; v < n; v++ {
		nt[u] = v + 1
		if Adj[u][v] == 1 {
			return v
		}
	}
	return -1
}

func _DFS(target int) {
	for i := 0; i < n; i++ {
		nt[i] = 0
	}
	stack := new(Stack)
	stack.container = make([]int, N)
	stack.push(target)
	statusColor[target] = GRAY
	clock++
	discoverTime[target] = clock
	for !stack.isEmpty() {
		u := stack.top()
		v := next(u)
		if v != -1 {
			if statusColor[v] == WHITE {
				statusColor[v] = GRAY
				clock++
				discoverTime[v] = clock
				stack.push(v)
			}
		} else {
			stack.pop()
			statusColor[u] = BLACK
			clock++
			finishTime[u] = clock
		}
	}
}

func DFS() {
	for i := 0; i < n; i++ {
		statusColor[i] = WHITE
		nt[i] = 0
	}
	clock = 0
	for i := 0; i < n; i++ {
		if statusColor[i] == WHITE {
			_DFS(i)
		}
	}
	for i := 0; i < n; i++ {
		fmt.Printf("%d %d %d\n", i+1, discoverTime[i], finishTime[i])
	}
}

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Split(bufio.ScanWords)
	scanner.Scan()
	n, _ = strconv.Atoi(scanner.Text())
	for i := 0; i < n; i++ {
		for j := 0; j < n; j++ {
			Adj[i][j] = 0
		}
	}
	for i := 0; i < n; i++ {
		scanner.Scan()
		u, _ := strconv.Atoi(scanner.Text())
		scanner.Scan()
		k, _ := strconv.Atoi(scanner.Text())
		u--
		for j := 0; j < k; j++ {
			scanner.Scan()
			v, _ := strconv.Atoi(scanner.Text())
			v--
			Adj[u][v] = 1
		}
	}

	DFS()
}

Python: def main():
    n=int(input())
    u,k,v=0,0,[[] for _ in range(n+1)]
    for i in range(n):
        l=list(map(int,input().split()))
        u=l[0]
        for j in l[2:]:
            v[u].append(j)
    d=[0]*(n+1)
    f=[0]*(n+1)

    def dfs(s,t):
        d[s]=t
        for i in v[s]:
            if d[i]==0:
                t=dfs(i,t+1)
        f[s]=t+1
        return t+1

    s,t=1,1
    while 0 in d[1:]:
        T=dfs(s,t)
        for i in range(1,n+1):
            if d[i]==0:
                s=i
                t=T+1
                break

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

if __name__ == '__main__':
    main()

Python: import sys
sys.setrecursionlimit(10000000)
n = int(input())
G = [[] for i in range(n)]
d = [0 for i in range(n)]
f = [0 for i in range(n)]
color = [0 for i in range(n)]
stamp = 0

def dfs(u):
    global stamp
    if d[u]:
        return
    stamp += 1
    d[u] = stamp
    for v in G[u]:
        if color[v] != 0:
            continue
        color[v] = 1
        dfs(v)
    color[u] = 2
    stamp += 1
    f[u] = stamp

color[0] = 1
for i in range(n):
    inp = list(map(int, input().split()))
    u = inp[0]
    u -= 1
    k = inp[1]
    for j in range(k):
        v = inp[j+2]
        v -= 1
        G[u].append(v)
        #G[v].append(u)


for i in range(n):
    dfs(i)

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

Python: """Depth First Search."""

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
    path.append(u)
    sndf[u][0] = True
    time += 1
    sndf[u].append(time)

    while path:
        u = path[-1]
        if adj[u]:
            v = adj[u].pop(0)
            if not sndf[v][0]:
                sndf[v][0] = True
                time += 1
                sndf[v].append(time)
                path.append(v)
        else:
            path.pop()
            sndf[u][0] = True
            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:])

Python: def curserch(N,k,t,P,n):
    g = 0
    t = t+1
    P[k]["d"] = t
    for i in range(1,n+1):
        if N[k][i]==1 and P[i]["d"]==0:
            P[i]["f"] = g = curserch(N,i,t,P,n)
    if g !=0:
        return g+1
    else:
        return t+1

n = int(input())
A = [[0 for j in range(n+1)] for i in range(n+1)]


for i in range(n):
    vec = input().split()
    u = int(vec[0])
    k = int(vec[1])
    nodes = vec[2:]
    for i in range(int(k)):
        v = int(nodes[i])
        A[u][v] = 1

P=[{"d":0} for i in range(n+1)]
P[1]["f"] = curserch(A,1,0,P,n)
j=1
for i in range(1,n+1):
    print(i,P[i]["d"],P[i]["f"])

Python: def disc(s,d):
	ans[d][0] = s
	s += 1
	if main[d][0] != 0:
		for l in range(1,int(main[d][0])+1):
			if ans[int(main[d][l])-1][0] == 0:
				s = disc(int(s),int(main[d][l])-1)
	ans[d][1] = s
	return s+1

n = int(input())

main = []

for i in range(n):
	a = input().split()
	if int(a[1]) > 0:
		main.append(a[1:])
	else:
		main.append([0])

del a

ans = [[0] * 2 for i in range(n)]

ls = disc(1,0)
for i in range(n):
	if ans[i][0] == 0:
		ls = disc(int(ls),i)


for i in range(n):
	print(str(i+1) + " " + str(ans[i][0]) + " " + str(ans[i][1]))

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

"""

input:
6
1 2 2 3
2 2 3 4
3 1 5
4 1 6
5 1 6
6 0

output:
1 1 12
2 2 11
3 3 8
4 9 10
5 4 7
6 5 6

"""
import sys

UNVISITED, VISITED_IN_STACK, POPPED_OUT = 0, 1, 2


def dfs(v_init):
    global timer

    # init the first node of overall graph iterations(times >= 1)
    stack = list()
    stack.append(v_init)
    d_time[v_init] += timer
    # init end

    while stack:
        current = stack[-1]
        v_table = adj_table[current]
        visited[current] = VISITED_IN_STACK
        # if adj is None, current's adj(s) have been all visited
        adj = v_table.pop() if v_table else None

        if adj:
            if visited[adj] is UNVISITED:
                visited[adj] = VISITED_IN_STACK
                timer += 1
                d_time[adj] += timer
                stack.append(adj)
        else:
            stack.pop()
            visited[current] = POPPED_OUT
            timer += 1
            f_time[current] += timer

    return None


def dfs_init():
    global timer
    for v in range(v_num):
        if visited[v + 1] == UNVISITED:
            timer += 1
            dfs(v + 1)


if __name__ == '__main__':
    _input = sys.stdin.readlines()
    v_num = int(_input[0])
    vertices = map(lambda x: x.split(), _input[1:])

    visited = [UNVISITED] * (v_num + 1)
    d_time, f_time = ([0] * (v_num + 1) for _ in range(2))
    adj_table = tuple([] for _ in range(v_num + 1))
    for v_info in vertices:
        v_idx, adj_num, *adj_list = map(int, v_info)
        # assert len(adj_list) == adj_num
        adj_table[v_idx].extend(sorted(adj_list, reverse=True))

    timer = 0
    dfs_init()

    for v_idx, time_info in enumerate(zip(d_time[1:], f_time[1:]), 1):
        print(v_idx, *time_info)