Go: package main

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

//util
var sc = bufio.NewScanner(os.Stdin)

func nextStr() string {
	sc.Scan()
	return sc.Text()
}

func nextInt() int {
	sc.Scan()
	i, e := strconv.Atoi(sc.Text())
	if e != nil {
		panic(e)
	}
	return i
}

func printList(list []int) {
	n := len(list)
	buf := bufio.NewWriter(os.Stdout)
	for _, v := range list[0 : n-1] {
		buf.Write([]byte(fmt.Sprintf("%d ", v)))
	}
	buf.Write([]byte(fmt.Sprintf("%d\n", list[n-1])))
	buf.Flush()
}

func max(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func min(a, b int) int {
	if a > b {
		return b
	} else {
		return a
	}
}

//solve

const MAX = 100
const WHITE = 0
const GRAY = 1
const BLACK = 2

var time, n int

//隣接行列
var M [MAX][MAX]int

//d[i]: 最初にiを訪問した時刻。発見時刻
//f[i]: iの隣接リストを訪問し終えた時刻。完了時刻
var color, d, f [MAX]int

func dfs_visit(u int) {
	color[u] = GRAY
	time++
	d[u] = time
	//隣接を調べる
	for v := 0; v < n; v++ {
		if M[u][v] == 0 {
			continue
		}
		//隣接するvが、未訪問だった場合
		if color[v] == WHITE {
			dfs_visit(v)
		}
	}
	//訪問完了処理
	color[u] = BLACK
	time++
	f[u] = time
	//訪問完了
}

func dfs() {
	//初期化
	for u := 0; u < n; u++ {
		color[u] = WHITE
	}
	time = 0

	//未訪問のuを始点として、深さ優先探索
	for u := 0; u < n; u++ {

		if color[u] == WHITE {
			dfs_visit(u)
		}
	}

	for u := 0; u < n; u++ {
		fmt.Println(u+1, d[u], f[u])
	}
}

func main() {
	sc.Split(bufio.ScanWords)
	n = nextInt()
	for i := 0; i < n; i++ {
		u, k := nextInt(), nextInt()
		M[u-1][u-1] = 0

		//fmt.Println(u, k)
		if k > 0 {
			for j := 0; j < k; j++ {

				v := nextInt()
				M[u-1][v-1] = 1

			}
		}

	}

	dfs()

}

Python: # -*- coding:utf-8 -*-

def solve():
    from collections import deque

    N = int(input())
    Us = [[] for _ in range(N)]

    for i in range(N):
        _in = list(map(int, input().split()))
        if len(_in) == 2:
            continue
        u, k, Vs = _in[0], _in[1], _in[2:]
        Vs.sort()
        for v in Vs:
            Us[u-1].append(v-1)
        Us[u-1] = deque(Us[u-1])

    find = [0 for _ in range(N)]  # 発見時刻
    terminated = [0 for _ in range(N)]  # 終了時刻
    tim = [0]  # 経過時間

    def dfs(now):
        while Us[now]:
            nxt = Us[now].popleft()

            if find[nxt] != 0:
                continue

            tim[0] += 1
            find[nxt] = tim[0]

            dfs(nxt)

        if terminated[now] == 0:
            tim[0] += 1
            terminated[now] = tim[0]

    for i in range(N):
        if find[i] == 0:
            tim[0] += 1
            find[i] = tim[0]
            dfs(i)

    for i in range(N):
        print(i+1, find[i], terminated[i])


if __name__ == "__main__":
    solve()

Python: # -*- coding: utf-8 -*-


class Graph:
    def __init__(self, n, adj_matrix):
        self.n = n
        self.adj_matrix = adj_matrix

    def show(self):
        for _ in self.adj_matrix:
            print(*_)

    def dfs(self):
        self.states = [2] * self.n
        self.d = [None] * self.n
        self.f = [None] * self.n
        self.time = 0

        for u in range(self.n):
            if self.states[u] == 2:
                self.dfs_visit(u)

    def dfs_visit(self, u):
        self.states[u] = 1
        self.time += 1
        self.d[u] = self.time

        for v, edge in enumerate(self.adj_matrix[u]):
            if edge and self.states[v] == 2:
                self.dfs_visit(v)

        self.states[u] = 0
        self.time += 1
        self.f[u] = self.time

    def show_dfs(self, start=0):
        self.dfs()
        for i, (d, f) in enumerate(zip(self.d, self.f)):
            print(i + 1, d, f)


if __name__ == '__main__':
    n = int(input())
    adj_matrix = [[0] * n for i in range(n)]
    for i in range(n):
        for v in input().split()[2:]:
            adj_matrix[i][int(v) - 1] = 1

    g = Graph(n, adj_matrix)
    g.show_dfs()

Python: def dfs():
    tt = 0
    for u in range(N):
        if colors[u] == WHITE:
            tt = dfs_visit(u, tt)

def dfs_visit(u, tt):
    tt += 1
    colors[u] = GLAY
    d[u] = tt
    for v in range(N):
        if adjacency_list[u][v] == 1 and colors[v] == WHITE:
            tt = dfs_visit(v, tt)
    tt += 1
    colors[u] = BLACK
    f[u] = tt
    return tt


N = int(input())
adjacency_list = [[0 for j in range(N)] for i in range(N)]
for _ in range(N):
    u, k, *v = map(int, input().split())
    for i in v:
        adjacency_list[u-1][i-1] = 1

WHITE = 0
GLAY = 1
BLACK = 2
colors = [WHITE for _ in range(N)]
d = [0 for _ in range(N)]
f = [0 for _ in range(N)]

dfs()

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

Python: n=int(input())

l=[[0 for i in range(n)] for j in range(n)]##静的確保 隣接行列
d=[0 for i in range(n)]##発見
f=[0 for i in range(n)]##完了
stack=[]##スタック
check=[]

for i in range(n):
    p=[int(x) for x in input().split()]
    
    for j in range(n):
        if j+1 in p[2:]:
            l[i][j]=1
##print("---隣接行列---")
##print(l)

time=1


while len(check)<n:
    for i in range(n):
        if i+1 not in check:
            stack+=[i+1]
            d[i]=time
            break
        else: pass

    while len(stack)!=0:
        ##print("----------while---------")
        for i in range(n):
            if l[stack[-1]-1][i]==1 and not i+1 in check and stack[-1]-1!=i:
                time+=1
                d[i]=time
                stack+=[i+1]

               ## print("stack:",stack)
               ## print("check:",check)
               ## print("d[",i,"]:",d[i])
                break
            elif i==n-1:
##最深    
                check+=[stack[-1]]
                time+=1
                f[stack.pop()-1]=time

               ## print("f[",i,"]:",f[i])
               ## print("check:",check)
    time+=1


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

Python: import sys
sys.setrecursionlimit(10**9)
from collections import deque

n = int(input())
u = [[] for i in range(n+1)] #隣接リスト
for i in range(n):
    v = list(map(int, input().split()))
    u[v[0]] = v[1:] #v = [次数, 頂点...]

d = [-1] * (n+1)
f = [-1] * (n+1)
visited = [False] * (n+1)
visited[0] = True
time = 1

#再帰
def dfs(c):
    global time
    d[c] = time
    time += 1
    visited[c] = True
    for i in range(1, u[c][0]+1):
        if not visited[u[c][i]]:
            dfs(u[c][i])
    f[c] = time
    time += 1
"""
探索は元の始点から到達可能なすべての頂点を発見するまで続き、
未発見の頂点が残っていれば、
その中の番号が一番小さい１つを新たな始点として探索を続けます。
"""
while False in visited:
    dfs(visited.index(False))

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

Python: def dfs_time(G, s):
    # ?????????
    color = {key: 'White' for key in G.keys()}
    d = {key: 0 for key in G.keys()}
    f = {key: 0 for key in G.keys()}

    # ????????????
    S = [s]
    color[s] = 'Gray'
    time = 1
    d[s] = time

    while len(S) > 0:
        u = S[-1]
        v_list = G[u]
        if v_list:
            v = v_list.pop()
        else: v = None
        if v is not None:
            if color[v] == 'White':
                color[v] = 'Gray'
                time += 1
                d[v] = time
                S.append(v)
        else:
            S.pop()
            color[u] = 'Black'
            time += 1
            f[u] = time

    return d, f


if __name__ == '__main__':
    G = {}
    n = int(input())
    for _ in range(n):
        tmp = list(map(int, input().split()))
        u = tmp.pop(0)
        k = tmp.pop(0)
        G[u] = tmp

    d, f= dfs_time(G, 1)

    for node in G:
        print('{0} {1} {2}'.format(node, d[node], f[node]))