Go: package main

import "fmt"

type node struct {
	id, d, v int
	adj      []node
}

var n int
var seen []bool
var graph []node

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func dfs(v, t int) int {
	// fmt.Printf("dfs(v %d, t %d) en:\n", v, t)
	seen[v] = true
	graph[v].v = t
	tt := t
	for _, vv := range graph[v].adj {
		if !seen[vv.id] {
			tt = max(tt, dfs(vv.id, tt+1))
		}
	}
	graph[v].d = tt + 1
	// fmt.Printf("dfs(v %d, t %d) ex: d %d\n", v, t, graph[v].d)
	return graph[v].d
}

func main() {
	fmt.Scan(&n)
	graph = make([]node, n+1)
	for i := 1; i <= n; i++ {
		var u, k, v int
		fmt.Scan(&u, &k)
		graph[u].id = u
		// 頂点uはk個の隣接頂点を持っている
		for j := 0; j < k; j++ {
			fmt.Scan(&v)
			var t node
			t.id = v
			graph[u].adj = append(graph[u].adj, t)
		}
	}
	// fmt.Println("n", n, "graph", graph)
	seen = make([]bool, n+1)
	// t := dfs(1, 1)
	v, t := 1, 1
	done := false
	for !done {
		tt := dfs(v, t)
		done = true
		for i := range seen {
			if !seen[i] {
				done = false
				v = graph[i].id
				t = tt
				break
			}
		}
	}
	for i := 1; i <= n; i++ {
		fmt.Printf("%d %d %d\n", i, graph[i].v, graph[i].d)
	}
}

C++: // http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_11_B

#include <iostream>
#include <iomanip>

#include <vector>
#include <string>
#include <iterator>
#include <algorithm>

typedef long long ll;
const long double PI = (acos(-1));
const long long MOD = 1000000007;
#define rep(i,n) REP(i,0,n)
#define REP(i,x,n) for(int i=x;i<n;++i)

using namespace std;

// change min/max
template<class T> inline bool chMin(T& a, T b) { if (a > b) { a = b; return true; } return false; }
template<class T> inline bool chMax(T& a, T b) { if (a < b) { a = b; return true; } return false; }

template<class T> void output(std::vector<T> & A) {
    const int n = A.size();
    for (int i = 0; i < n - 1; ++i) {
        std::cout << A[i] << " ";
    }
    std::cout << A[n - 1] << std::endl;
}

const int WHITE = 0;
const int GRAY  = 1;
const int BLACK = 2;
vector<vector<int>> M;
vector<int> colour, d, f;
int tt = 0;

void dfs(int u) {
    // if black exit
    if (colour[u] == BLACK) return;
    
    // if white
    if (colour[u] == WHITE) {
        colour[u] = GRAY;
        d[u] = tt++;
        const int nb = M[u].size();
        rep(i, nb) {
            dfs(M[u][i]);
        }
        // if you are here, all the neigbours are already checked
        f[u] = tt++;
        colour[u] = BLACK;
    }
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout << std::fixed << std::setprecision(15);

    // populate M
    int n; cin >> n;
    M = vector<vector<int>>(n);
    colour = vector<int>(n, 0);
    d = vector<int>(n, -1);
    f = vector<int>(n, -1);

    rep(i, n) {
        int from; cin >> from; --from;
        int m; cin >> m;
        rep(j, m) {
            int to; cin >> to; --to;
            M[from].push_back(to);
            // M[to].push_back(from);
        }
    }

    // dfs
    rep(i, n) {
        if (colour[i] == WHITE) {
            dfs(i);
        }
    }

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

    return 0;
}