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) isVisited() bool {
	return v.firstVisitAt > 0
}

func dfs(currentId vertexId, timestamp int, vertices []vertex) int {
	currentVertex := &vertices[currentId-1]
	if currentVertex.isVisited() {
		return timestamp
	}
	timestamp = timestamp + 1
	currentVertex.firstVisitAt = timestamp
	for _, adjId := range currentVertex.adjs {
		timestamp = dfs(adjId, timestamp, vertices)
	}
	timestamp = timestamp + 1
	currentVertex.finishVisitAt = timestamp
	return timestamp
}

func solveDfs(vertices []vertex) {
	timestamp := 0
	for {
		vid := vertexId(-1)
		for i := 0; i < len(vertices); i++ {
			if !vertices[i].isVisited() {
				vid = vertexId(i+1)
				break
			}
		}
		if vid > 0 {
			timestamp = dfs(vid, timestamp, vertices)
		} else {
			break
		}
	}
}

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

	solveDfs(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 <stack>

using namespace std;

#define MAX 110
int num;
int matrix[MAX][MAX];
int dst[MAX];

void input(){
    cin >> num;
    for(int i=0;i<MAX;i++){
        dst[i] = -1;
        for(int j=0;j<MAX;j++){
            matrix[i][j] = 0;
        }
    }
    for(int i=1;i<=num;i++){
        int v; cin >> v;
        int tmp; cin >>tmp;
        for(int j=0;j<tmp;j++){
            int vi; cin >> vi;
            matrix[v-1][vi-1] = 1;
        }
    }
}

void dfs(){
    stack<int> S;
    S.push(0);
    dst[0] = 0;
    while(!S.empty()){
        int cur = S.top();
        S.pop();
        if(dst[cur]==-1 || dst[cur]== 0){
            for(int i=0;i<num;i++){
                if(matrix[cur][i]==1){
                    S.push(i);
                    dst[i] = dst[cur] + 1;
                }
            }
        }
        else if(dst[S.top()]+1 < dst[cur]){
            for(int i=0;i<num;i++){
                if(matrix[cur][i]==1){
                    if(dst[cur] +1 <= dst[i]){
                        S.push(i);
                        dst[i] = dst[cur] + 1;
                    }
                }
            }
        }
    }
    for(int i=0;i<num;i++){
        cout << i+1 << " " <<  dst[i] << endl;
    }
}

int main(){
    input();
    dfs();
}

C++: #include<bits/stdc++.h>
#include<limits.h>
using namespace std;
#define rep(i, n) for(int i = 0; i < n; i++)

#define MAX 200

vector<vector<int>> adj_mat(MAX, vector<int>(MAX, 0));
vector<int> d(MAX, -1); //頂点を訪問したステップを記録
vector<int> f(MAX, -1); //頂点の隣接リストを探索し終えたステップを記録

int n;
int step = 1;

void dfs(int init_node){
    stack<int> s;
    int node = init_node;
    s.push(node);
    d.at(node) = step;

    int next_node = -1;
    while(1){
        step++;

        next_node = -1;
        rep(i, n){
            if(adj_mat.at(node).at(i) == 1 && d.at(i) == -1){
                next_node = i;
                break;
            }
        }

        if(next_node == -1){        //新たなノードがない場合
            s.pop();            //次のノードが存在しないノードをスタックから削除
            if(s.size() == 0){
                f.at(node) = step;
                step++;
                break;
            }
            next_node = s.top();

            f.at(node) = step;   // 新たなノードがないので訪問ずみ
            // cout << "node not found " << node+1 << "    " << next_node+1 << endl;
        }else{                      //新たなノードがある場合
            s.push(next_node);
            d.at(next_node) = step; // 初めて見つけたノードとして記録
            // cout << "node found " << node+1 << " --> " << next_node+1 << endl;
        }
        
        node = next_node;
    }
}

int main(){
    cin >> n;

    // 隣接行列の作成
    rep(i, n){
        int u, k; cin >> u >> k;
        rep(j, k){
            int v; cin >> v;
            adj_mat.at(u-1).at(v-1) = 1;
        }
    }


    rep(i, n){
        if(d.at(i) == -1){
            dfs(i);
        }
    }

    rep(i, n){
        cout << i + 1 << " " << d.at(i) << " " << f.at(i) << endl;
    }


}

C++: #include <iostream>
#include <vector>
using namespace std;
vector<vector<int>> adjlist;
int t=0;
vector<int> d;
vector<int> f;
void input(int n){
	vector<int> f={0,0};
	adjlist.push_back(f);
  for(int i=0;i<n;i++){
    vector<int> list;
    int ln;
    cin>>ln;
    list.push_back(ln);
    cin>>ln;
    list.push_back(ln);
    for(int j=0;j<ln;j++){
      int an;
      cin>>an;
      list.push_back(an);
    }
    adjlist.push_back(list);
  }
}

void depth_search(int id){
	vector<int> list=adjlist[id];
	for(int j=0;j<list[1];j++){
		if(d[list[2+j]]<0){
			d[list[2+j]]=++t;
			depth_search(list[2+j]);
		}
	}
	f[id]=++t;
}
 
void print(int n){
  for(int i=1;i<n+1;i++){
    cout<<i<<' '<<d[i]<<' '<<f[i]<<endl;
  }
}
int main(){
  int n;
  cin>>n;
	d=vector<int> (n+1,-1);
	f=vector<int> (n+1,-1);
  input(n);
	for(int i=1;i<n+1;i++){
		if(f[i]<0){
			d[i]=++t;
			f[i]=t;
			depth_search(i);
		}
	}
  print(n);
  return 0;
}