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))
}

C++: #include <iostream>
#include <cstdio>
#include <vector>
#define N 100
typedef struct SearchTime SearchTime;

struct SearchTime{
    int find = -1;
    int comp = -1;
    bool flag = false;
    void print(){
        std::cout << find << " " << comp << '\n';
    }
};

int cnt = 1;
int A[N][N];
SearchTime st[N];

void input_graph(std::vector<std::vector<int> >& Adj, int n){
    int u, k, v;
    for(int i=0; i<n; i++){
        std::cin >> u >> k;
        for(int j=0; j<k; j++){
            std::cin >> v;
            Adj[u-1].push_back(v);
        }
    }
}

void input_A(std::vector<std::vector<int> >& Adj, int n){
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            A[i][j] = 0;
        }
    }
    for(int i=0; i<n; i++){
        int len = Adj[i].size();
        for(int j=0; j<len; j++){
            A[i][Adj[i][j]-1] = 1;
            A[Adj[i][j]-1][i] = -1;
        }
    }
}

void output_A(int n){
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            if(j+1==n)  printf("%3d\n", A[i][j]);
            else        printf("%3d", A[i][j]);
        }
    }
}

void result(int n){
    for(int i=0; i<n; i++){
        std::cout << i+1 << " ";
        st[i].print();
    }
}

void DFS(std::vector<std::vector<int> >& Adj, int n){
    int i = 0;
    int j;
    int nexti;              //毎回-1する.
    std::vector<int> tmp;   //軌跡を保存
    //深さ優先探索
    while(cnt <= n*2){
        //下層へ探索
        //std::cout << "\nstrat\n";
        while(i != -1){
            st[i].flag = true;
            st[i].find = cnt;
            cnt++;
            //std::cout << "cnt:" << cnt-1 << '\n';
            tmp.push_back(i);
            //std::cout << "tmp.size():" << tmp.size() << '\n';
            //std::cout << "i:" << i+1 << '\n';
            //次の葉を探す
            if((int)Adj[i].size() != 0){//葉が最深でないとき
                j = 0;
                nexti = Adj[i][j]-1;
                while(st[nexti].flag){
                    j++;
                    if(j == (int)Adj[i].size()){//もし葉がすべて探索済みなら
                        st[i].comp = cnt;
                        cnt++;
                        //std::cout << "tochu\n";
                        //std::cout << "cnt:" << cnt-1 << '\n';
                        i = -1; //2つ目のwhile文抜ける
                        break;
                    }
                    nexti = Adj[i][j]-1;
                }
                if(i != -1) i = nexti;
            }
            else{//葉が最深のとき
                st[i].comp = cnt;
                cnt++;
                i = -1; //2つ目のwhile文抜ける
                //std::cout << "saishin\n";
                //std::cout << "cnt:" << cnt-1 << '\n';
                //std::cout << "tmp.size():" << tmp.size() << '\n';
                //std::cout << "i:" << i << '\n';
            }
        }
        //ここから根に向かって戻る. 未探索の葉があればそちらへ行く
        //std::cout << "\nback\n";
        tmp.pop_back();
        i = (int)tmp.back();
        tmp.pop_back();
        //std::cout << "tmp.size():" << tmp.size() << '\n';
        //std::cout << "i:" << i+1 << '\n';
        j = 0;
        nexti = Adj[i][j]-1;
        while(st[nexti].flag){
            j++;
            if(j == (int)Adj[i].size()){//現在のノードから未探索の葉がない
                if((int)tmp.size() != 0){//まだtmpに軌跡が残っていれば
                    st[i].comp = cnt;
                    cnt++;
                    //std::cout << "cnt:" << cnt-1 << '\n';
                    i = (int)tmp.back();
                    tmp.pop_back();
                    //std::cout << "tmp.size():" << tmp.size() << '\n';
                    //std::cout << "i:" << i+1 << '\n';
                    j = 0;
                    nexti = Adj[i][j]-1;
                }
                else{//残っていないなら
                    st[i].comp = cnt;
                    cnt++;      //たぶんこれで終わる
                    //std::cout << "cnt:" << cnt-1 << '\n';
                    break;
                }
            }
            else{
                nexti = Adj[i][j]-1;
            }
        }
        tmp.push_back(i);
        i = nexti;
    }
}


int main(){
    int n;
    std::cin >> n;
    std::vector<std::vector<int> > Adj;
    Adj.resize(n);
    input_graph(Adj, n);
    //input_A(Adj, n);
    //output_A(n);
    DFS(Adj, n);
    result(n);
    return 0;
}