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: n=int(input())

next=[[] for i in range(n)]
for i in range(n):
	l=list(map(int,input().split()))
	u=l[0]
	k=l[1]
	for j in range(2,k+2):
		next[u-1].append(l[j]-1)

stk=[]
stk.append(0)
time=1
d=[0 for i in range(n)]
f=[0 for i in range(n)]
know=[0 for i in range(n)]

def dfs(x):
	global time
	know[x]=1
	d[x]=time
	time+=1
	for n in next[x]:
		if know[n]==0:
			dfs(n)
	f[x]=time
	time+=1
	
for i in range(n):
	if know[i]==0:
		dfs(i)

for i in range(n):
	out=[]
	out.append(i+1)
	out.append(d[i])
	out.append(f[i])
	print(*out)

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

import sys
sys.setrecursionlimit(10 ** 9)
def input(): return sys.stdin.readline().strip()
def INT(): return int(input())
def MAP(): return map(int, input().split())
def LIST(): return list(map(int, input().split()))
INF=float('inf')

N=INT()
nodes=[[] for i in range(N)]
for i in range(N):
    l=LIST()
    u=l[0]
    l=l[2:]
    for v in l:
        nodes[u-1].append(v-1)
    nodes[u-1].sort()

ans=[[0]*2 for i in range(N)]
visited=[False]*N
time=0
def rec(u):
    if visited[u]:
        return
    visited[u]=True
    global time
    time+=1
    ans[u][0]=time
    for v in nodes[u]:
        rec(v)
    time+=1
    ans[u][1]=time

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

Python: #-*- coding: utf-8 -*-
"""
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_11_B
"""
import itertools
import time
import logging

def input_from_txt(number=1):
    import requests
    url = "https://judgedat.u-aizu.ac.jp/testcases/ALDS1_11_B/%s/in" % number
    res = requests.get(url)
    input = res.text.split("\n")
    n = int(input[0])
    Ss = []
    for i in range(n):
        S = list(map(int, input[1+i].split()))
        Ss.append(S)
    return (n, Ss)

def input_array():
    n = int(input())
    Ss = []
    for i in range(n):
        S = list(map(int, input().split()))
        Ss.append(S)
    return (n, Ss)

def show_matrix(n, Ss):
    for i in range(n):
        vth = Ss[i][0]
        vd = Ss[i][1]
        adjs = Ss[i][2:]
        row = []
        for j in range(1, 1+n):
            if j in adjs: row.append(1)
            else: row.append(0)
        logging.info(" ".join(map(str, row)))

class Verticies:
    def __init__(self, id, value, degree, adjs:list):
        self.id = id
        self.value = value
        self.degree = degree
        self.d = 0 # timesamp
        self.f = 0 # timesamp
        self.adjs = adjs

    def visit(self, ts):
        logging.debug("miso0a: visiting id(%s)" % self.id)
        if not self.is_visited():
            self.d = ts
            logging.debug("visited id(%s) and ts stamped as %s" % (self.id, ts))
            logging.info("id:%s d:%s f:%s (preorder)" % (self.id, self.d, self.f))
            ts+=1
            logging.debug("miso1: ts incremented as %s" % ts)
        return ts

    def all_visit(self, ts):
        if self.adjs_all_visited() and not self.adjs_all_stamped():
            self.f = ts
            logging.info("id:%s d:%s f:%s (=================)" % (self.id, self.d, self.f))
            ts +=1
        return ts

    def is_visited(self):
        logging.debug ("miso9: id(%s) is_visited: %s(%s)" % (self.id, not self.d == 0, self.d))
        return (not self.d == 0)

    def adjs_all_visited(self):
        visited = [v.is_visited() for v in self.adjs]
        logging.debug("miso5: %s" % self.adjs)
        logging.debug("miso(visited): %s" % visited)
        completed = all(visited)
        logging.debug("miso(completed): %s" % completed)
        return completed

    def adjs_all_stamped(self):
        stamped = [v.f != 0 for v in self.adjs]
        logging.debug("--- miso5 ---: %s" % [x.f for x in self.adjs])
        logging.debug("miso(stamped): %s" % stamped)
        completed = all(stamped)
        logging.debug("miso(completed): %s" % completed)
        return completed

    def get_adjs_info(self):
        return zip([v.id for v in self.adjs], [v.f for v in self.adjs])




def instanciate_vertex(V:list):
    vertex = {}
    for v in V:
        id = int(v[0])
        degree = v[1]
        adjs = v[2:]
        vertices = Verticies(id, 0, degree, adjs)
        vertex[id] = vertices
    # switch from string to Instance
    for id, vertices in vertex.items():
        vertex[id].adjs = [ vertex[i] for i in vertex[id].adjs]
    # sort by young id ascendence
    return sorted(list(vertex.values()), key=lambda v: v.id)


ANSWER={}
def dfs(v: Verticies, ts, stack:list):
    logging.info("dfs(v.id:%s, ts:%s, stack:%s)" % (v.id, ts, [ x.id for x in stack]))
    if not v.is_visited():
        ts = v.visit(ts)

    if len(v.adjs) == 0 or (v.adjs_all_visited()):
        v.f = ts
        logging.warn("id:%s d:%s f:%s (=================)" % (v.id, v.d, v.f))
        ANSWER[v.id] = (v.id, v.d, v.f)
        ts +=1
        stack.pop()
    else:
        logging.info("adjs_info:%s= (========not yet =======)" % ([(a,b) for a, b in v.get_adjs_info()] ))

    for next_v in v.adjs:
        if not next_v.is_visited():
            stack.append(next_v)
            break
    # ts = v.visit(ts)
    # ts = v.all_visit(ts)

    return ts

def dfs_all(vertex:list):
    stack = []
    #stack.append(vertex[0])
    ts = 1
    fin = False
    while not fin:
        for i, v in enumerate(vertex):
            logging.info("i:%s == len(vertex)-1: %s" % ( i, len(vertex)-1))
            if not v.is_visited():
                stack.append(v)
                break
            if i == len(vertex)-1: fin = True
        while len(stack) > 0:
            v = stack[len(stack)-1]
            ts = dfs(v, ts, stack)
            logging.info("while stack non empty")
            #time.sleep(1)
        logging.info("while True")
        #time.sleep(1)


if __name__ == '__main__':
    logging.getLogger().setLevel(logging.ERROR)
    #mode = "test"
    mode = "prod"
    if mode == "test":
        (n, Ss) = input_from_txt(1)
        show_matrix(n, Ss)
        logging.getLogger().setLevel(logging.INFO)
        #logging.getLogger().setLevel(logging.DEBUG)
    else:
        (n, Ss) = input_array()

    vertex = instanciate_vertex(Ss)
    logging.debug(vertex)
    dfs_all(vertex)
    from collections import OrderedDict
    for id, d, f in OrderedDict(sorted(ANSWER.items())).values():
        print("%s %s %s" % (id, d, f))
    #dfs_all(vertex)