Go: package main

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

type vertexId int

type vertex struct {
	id vertexId
	firstVisitAt int
	finishVisitAt int
	adjs []vertexId
}

func createVertex(id int, adjsInt []int) vertex {
	adjs := make([]vertexId, len(adjsInt))
	for i := 0; i < len(adjs); i++ {
		adjs[i] = vertexId(adjsInt[i])
	}
	return vertex{
		id: vertexId(id),
		firstVisitAt: 0,
		finishVisitAt: 0,
		adjs: adjs,
	}
}

func (v *vertex) doesAllAdjsVisited(vertices []vertex) bool {
	for _, vId := range v.adjs {
		adj := vertices[vId-1]
		if !adj.isVisited() {
			return false
		}
	}
	return true
}

func (v *vertex) isFirstVisit() bool {
	return v.firstVisitAt == 0
}

func (v *vertex) isVisited() bool {
	return v.firstVisitAt > 0
}

func (v *vertex) nextVertex(vertices []vertex) (vertexId, error) {
	for _, vId := range v.adjs {
		adj := vertices[vId-1]
		if !adj.isVisited() {
			return vId, nil
		}
	}
	return vertexId(-1), fmt.Errorf("illegal")
}

func dfs(currentId vertexId, timestamp int, vertices []vertex) int {
	timestamp = timestamp + 1
	currentVertex := &vertices[currentId-1]
	if currentVertex.isFirstVisit() {
		currentVertex.firstVisitAt = timestamp
	}
	for !currentVertex.doesAllAdjsVisited(vertices) {
		nextVertexId, _ := currentVertex.nextVertex(vertices)
		timestamp = dfs(nextVertexId, timestamp, vertices)
	}
	timestamp = timestamp + 1
	currentVertex.finishVisitAt = timestamp
	return timestamp
}

func main() {
	sc := bufio.NewScanner(os.Stdin)
	sc.Split(bufio.ScanWords)

	n, err := nextInt(sc)
	if err != nil {
		panic(err)
	}

	vertices := make([]vertex, n)
	for i := 0; i < n; i++ {
		id, err := nextInt(sc)
		if err != nil {
			panic(err)
		}
		adjN, err := nextInt(sc)
		if err != nil {
			panic(err)
		}
		adjs := make([]int, adjN)
		for j := 0; j < adjN; j++ {
			adjId, err := nextInt(sc)
			if err != nil {
				panic(err)
			}
			adjs[j] = adjId
		}
		vertices[i] = createVertex(id, adjs)
	}

	dfs(vertexId(1), 0, vertices)

	for i := 0; i < n; i++ {
		fmt.Printf("%d %d %d\n", vertices[i].id, vertices[i].firstVisitAt, vertices[i].finishVisitAt)
	}
}

func nextText(sc *bufio.Scanner) string {
	sc.Scan()
	return sc.Text()
}

func nextInt(sc *bufio.Scanner) (int, error) {
	return strconv.Atoi(nextText(sc))
}

Python: # http://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=4647653#1



import sys
sys.setrecursionlimit(10 ** 8)
input = sys.stdin.readline

class Graph:
    def __init__(self, n, dictated=False, decrement=True, edge=[]):
        self.n = n
        self.dictated = dictated
        self.decrement = decrement
        self.edge = [set() for _ in range(self.n)]
        self.parent = [-1]*self.n
        self.info = [-1]*self.n
        for x, y in edge:
            self.add_edge(x,y)

    def add_edge(self,x,y):
        if self.decrement:
            x -= 1
            y -= 1
        self.edge[x].add(y)
        if self.dictated == False:
            self.edge[y].add(x)

    def add_adjacent_list(self,i,adjacent_list):
        if self.decrement:
            self.edge[i] = set(map(lambda x:x-1, adjacent_list))
        else:
            self.edge[i] = set(adjacent_list)

    def path_detector(self, start=0, time=0):
        """
        :param p: スタート地点
        :return: 各点までの距離と何番目に発見したかを返す
        """

        edge2= []
        for i in range(self.n):
            edge2.append(sorted(self.edge[i], reverse=True))

        p, t = start, time
        self.parent[p] = -2
        full_path = [(p + self.decrement,t)]
        while True:
            if edge2[p]:
                q = edge2[p].pop()
                if q == self.parent[p] and not self.dictated:
                    """ 逆流した時の処理 """
                    """"""""""""""""""""
                    continue
                if self.parent[q] != -1:
                    """ サイクルで同一点を訪れた時の処理 """
                    """"""""""""""""""""
                    continue
                self.parent[q] = p
                p, t = q, t + 1
                full_path.append((p + self.decrement, t))
            else:
                """ 探索完了時の処理 """
                full_path.append((p + self.decrement, t))
                """"""""""""""""""""
                if p == start and t == time:
                    break
                p, t = self.parent[p], t-1
                """ 二度目に訪問時の処理 """
                """"""""""""""""""""
        return full_path

    def path_list(self):
        """
        :return: 探索経路を返す。
        """
        self.parent = [-1]*self.n
        res = []
        for p in range(self.n):
            if self.parent[p] == -1:
                res.append(self.path_detector(start=p, time=1))
        return res

    def draw(self):
        """
        :return: グラフを可視化
        """
        import matplotlib.pyplot as plt
        import networkx as nx

        if self.dictated:
            G = nx.DiGraph()
        else:
            G = nx.Graph()
        for x in range(self.n):
            for y in self.edge[x]:
                G.add_edge(x + self.decrement, y + self.decrement)
        nx.draw_networkx(G)
        plt.show()

def make_graph(dictated=False, decrement=True):
    """
    自己ループの無いグラフを生成。N>=2
    :param dictated: True = 有効グラフ
    :param decrement: True = 1-indexed
    :return:
    """
    import random
    N = random.randint(2, 5)
    if N > 2:
        M_max = (3*N-6)*(1+dictated)
    else:
        M_max = 1
    graph = Graph(N, dictated, decrement)
    for _ in range(random.randint(0,M_max)):
        graph.add_edge(*random.sample(range(decrement, N+decrement), 2))
    return graph

##################################################################
# 入力が隣接リストの場合
##################################################################

N = int(input())  # 頂点数

graph = Graph(N,dictated=True, decrement=True)

for i in range(N):  # [[頂点1と連結している頂点の集合], [頂点2と連結している頂点の集合],...]
    points = list(map(int, input().split()))[2:]
    graph.add_adjacent_list(i, points)


data = graph.path_list()

from itertools import chain
data = list(chain.from_iterable(data))

res = [[i+1,0,0] for i in range(N)]
for time, a in enumerate(data, start=1):
    i = a[0] - 1
    if res[i][1]:
        res[i][2] = time
    else:
        res[i][1] = time

for a in res:
    print(*a)

Python: from sys import stdin
n = int(stdin.readline())
M = [None]
M += [list(map(int, stdin.readline().split()[2:])) for i in range(n)]
sndf = [None]
sndf += [[False, i] for i in range(1, n + 1)]
tt = 0
def dfs(u):
    global tt
    sndf[u][0] = True
    tt += 1
    sndf[u].append(tt)
    for v in M[u]:
        if not sndf[v][0]:
            dfs(v)
    tt += 1
    sndf[u].append(tt)
for i in range(1, n + 1):
    if not sndf[i][0]:
        dfs(i)
    print(*sndf[i][1:])

Python: def depth(s, time):
    time += 1
    flag[s] = 1
    d[s] = t
    for i in range(1, n+1):
        if G[s][i] == 1:
            if flag[i] == 0:
                depth(i, time)
                t = f[i]
    f[s] = time + 1
 
n = int(raw_input())
G = [[0 for i in range(n+1)] for j in range(n+1)]
d = [0 for i in range(n+1)]
f = [0 for i in range(n+1)]
 
for i in range(n):
    v = map(int, raw_input().split())
    for j in range(v[1]):
        G[v[0]][v[2+j]] = 1
 
flag = [0 for i in range(n+1)]
for i in range(1, n+1):
    if flag[i] == 0:
        depth(i, max(f))
 
for i in range(1, n+1):
    print(str(i) + " " + str(d[i]) + " " + str(f[i]))