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

C++: #include <iostream>
#include <vector>
using namespace std;

vector<vector<int>> G;
vector<int> used;
vector<int> found;
vector<int> done;

int dfs(int v, int t){
    if(!used[v]){
        used[v] = 1;
        found[v] = t;
    }
    for(long long i=0; i<G[v].size(); i++){
        int nv = G[v][i];
        if(used[nv]) continue;
        else{
            t = dfs(nv, ++t);
        }
    }
    return done[v] = ++t;
}

int main(){
    int N; cin>>N;
    G.resize(N);
    used.resize(N, 0);
    found.resize(N, 0);
    done.resize(N, 0);
    found[0] = 1;
    for(long long i=0; i<N; i++){
        int u, k; cin >> u >>k;
        u--;
        G[u].resize(k);
        for(long long j=0; j<k; j++){
            int v; cin >> v;
            v--;
            G[u][j] = v;
        }
    }
    dfs(0, 1);
    for(long long i=0; i<N; i++){
        cout << i+1 << " " << found[i] << " " << done[i] << endl;
    }
    return 0;
}

C++: /* ALDS1_11_B: Depth First Search */

#include <iostream>
#include <cstdio>
#include <vector>
using std::vector;

vector<int> *graph;
int elapsed = 1;

// Search the graph recursively by depth-first search.
void traverse(int id, int found[], int finished[]) {
    if (0 < found[id]) {
        return;
    }

    found[id] = elapsed++;
    vector<int> adjs = graph[id];
    for (int i = 0; i < adjs.size(); i++) {
        traverse(adjs[i], found, finished);
    }
    finished[id] = elapsed++;
}

int main() {
    int n_v;
    scanf("%d", &n_v);

    int max_v = n_v + 1;
    graph = new vector<int>[max_v];

    for (int i = 0; i < max_v; i++) {
        int id, n_adjs;
        scanf("%d %d", &id, &n_adjs);

        for (int j = 0; j < n_adjs; j++) {
            int adj_id;
            scanf("%d", &adj_id);
            graph[id].push_back(adj_id);
        }
    }

    int found[max_v];
    int finished[max_v];
    std::fill_n(found, max_v, 0);
    std::fill_n(finished, max_v, 0);

    for (int id = 1; id < max_v; id++) {
        traverse(id, found, finished);
    }

    for (int id = 1; id < max_v; id++) {
        printf("%d %d %d\n", id, found[id], finished[id]);
    }
    return 0;
}

C++: #include<algorithm>
#include<bitset>
#include<iostream>
#include<iomanip>
#include<set>
#include<string>
#include<math.h>

using namespace std;

class zahyo{
	public:
	int x;
	int y;
	zahyo(){
		x=0;
		y=0;
	}
	zahyo(int x_,int y_){
		x=x_;
		y=y_;
	}
	void print(){
		cout<<x<<" "<<y;
	}
	zahyo operator+(zahyo add){
		add.x=this->x+add.x;
		add.y=this->y+add.y;
		return add;
	}
	zahyo operator-(zahyo add){
		add.x=this->x-add.x;
		add.y=this->y-add.y;
		return add;
	}
	bool operator==(const zahyo add) const{
		if(add.x==this->x and add.y==this->y){return true;}
		else{return false;}
	}
	bool operator!=(const zahyo add) const{
		if(add.x==this->x or add.y==this->y){return false;}
		else{return true;}
	}
	bool operator<(const zahyo add) const{
		if(this->x<add.x){return true;}
		else if(this->x==add.x and this->y<add.y){return true;}
		else{return false;}
	}
	bool operator>(const zahyo add) const{
		if(this->x>add.x){return true;}
		else if(this->x==add.x and this->y>add.y){return true;}
		else{return false;}
	}
	void input(){
		cin>>x>>y;
	}
};

int n;
int counter=0;
int glaph[100][100]={{}};
int ans[100][2]={{}};
void depthsearch(int t){
	counter++;
	ans[t][0]=counter;
	for(int i=0;i<n;i++){
		if(glaph[t][i]==1 and ans[i][0]==0){
			depthsearch(i);
		}
	}
	counter++;
	ans[t][1]=counter;
}

int main(){
	cin>>n;
	for(int i=0;i<n;i++){
		int k;
		cin>>k;
		cin>>k;
		for(int j=0;j<k;j++){
			int l;
			cin>>l;
			l--;
			glaph[i][l]=1;
		}
	}
	for(int i=0;i<n;i++){
		if(ans[i][0]==0){
			depthsearch(i);
		}
	}
	for(int i=0;i<n;i++){
		cout<<(i+1)<<" "<<ans[i][0]<<" "<<ans[i][1]<<endl;
	}
}

C++: #include <iostream>
#include <vector>

using namespace std;

void dfs(vector<vector<int>>& graph, int vertex, vector<bool>& marked, vector<int>& d, vector<int>& f, int& time) {
    if (marked[vertex])
        return;
    marked[vertex] = true;
    d[vertex] = ++time;
    for (int x: graph[vertex])
        if (!marked[x])
            dfs(graph, x, marked, d, f, time);
    f[vertex] = ++time;
}

int main() {
    int n;
    cin >> n;

    vector<vector<int>> graph;
    for (int i = 0; i < n; i++) {
        vector<int> t;
        int u;
        cin >> u;
        int k;
        cin >> k;
        for (int j = 0; j < k; j++) {
            int v;
            cin >> v;
            t.push_back(v - 1);
        }
        graph.push_back(t);
    }

    vector<bool> marked(n);
    vector<int> d(n);
    vector<int> f(n);
    int time = 0;

    for (int i = 0; i < n; i++)
        dfs(graph, i, marked, d, f, time);
    for (int i = 0; i < n; i++)
        cout << i + 1 << " " << d[i] << " " << f[i] << endl;

    return 0;
}