Go: package main

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

var scanner = bufio.NewScanner(os.Stdin)

func nextString() string {
	scanner.Scan()
	return scanner.Text()
}

func nextInt() int {
	n, err := strconv.Atoi(nextString())
	if err != nil {
		fmt.Printf("strconv.Atoi failed: %v\n", err)
	}
	return n
}

var infty = 1000000000

type nodeKey int

type DirectedGraphNode struct {
	key       nodeKey
	adjacents []*DirectedGraphNode
	visited   bool
	d         int
	f         int
	parent    *DirectedGraphNode
}

type DirectedGraph struct {
	nodes []*DirectedGraphNode
}

func (g *DirectedGraph) setAdjacency(l, r nodeKey) {
	g.nodes[l].adjacents = append(g.nodes[l].adjacents, g.nodes[r])
}

func newDirectedGraph(nNodes int) *DirectedGraph {
	graph := DirectedGraph{}
	graph.nodes = make([]*DirectedGraphNode, nNodes)
	for iNodes := 0; iNodes < nNodes; iNodes++ {
		graph.nodes[iNodes] = &DirectedGraphNode{}
		graph.nodes[iNodes].key = nodeKey(iNodes)

		graph.nodes[iNodes].visited = false
		graph.nodes[iNodes].d = infty
		graph.nodes[iNodes].f = infty
		graph.nodes[iNodes].parent = nil
	}
	return &graph
}

type QueueEntry struct {
	node *DirectedGraphNode
	next *QueueEntry
}

type Queue struct {
	head, tail *QueueEntry
}

func (q *Queue) enqueue(node *DirectedGraphNode) {
	entry := QueueEntry{}
	entry.node = node

	if q.tail == nil {
		q.head = &entry
		q.tail = &entry
		return
	}

	q.tail.next = &entry
	q.tail = q.tail.next
}

func (q *Queue) dequeue() *QueueEntry {
	if q.head == nil {
		return nil
	}
	poped := q.head
	q.head = q.head.next
	if q.head == nil {
		q.tail = nil
	}
	return poped
}

func (q *Queue) isEmpty() bool {
	return q.head == nil
}

var time = 0

func fillDistancesByDepthFirstSearch(g *DirectedGraph) {
	for _, u := range g.nodes[1:] {
		if !u.visited {
			dfsVisit(g, u)
		}
	}
}

func dfsVisit(g *DirectedGraph, u *DirectedGraphNode) {
	time++
	u.d = time

	u.visited = true
	for _, v := range u.adjacents {
		if !v.visited {
			v.parent = u
			dfsVisit(g, v)
		}
	}
	time++
	u.f = time
}

func main() {
	scanner.Split(bufio.ScanWords)

	nNodes := nextInt()
	g := newDirectedGraph(nNodes + 1)

	for iNode := 0; iNode < nNodes; iNode++ {
		nodeKeyLeft := nodeKey(nextInt())
		nAdj := nextInt()
		for iAdj := 0; iAdj < nAdj; iAdj++ {
			nodeKeyRight := nodeKey(nextInt())
			g.setAdjacency(nodeKeyLeft, nodeKeyRight)
		}
	}

	fillDistancesByDepthFirstSearch(g)

	for iNode := 1; iNode <= nNodes; iNode++ {
		node := g.nodes[iNode]
		fmt.Println(node.key, node.d, node.f)
	}
}

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)