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++: #include "bits/stdc++.h"
#define REP(i, n) for(int i = 0; i < n; i++)
#define REPR(i, n) for(int i = n; i >= 0; i--)
#define FOR(i, m, n) for(int i = m; i < n; i++)
#define FORR(i, a, b) for(int i=(b)-1; i>=(a); --i)
#define ALL(v) v.begin(), v.end()
using namespace std;
typedef long long LL;
typedef long double LD;
typedef vector<int> VI;
typedef vector<LL> VLL;
typedef vector<vector<int> > VVI;
typedef vector<string> VS;
typedef pair<int, int> PII;
typedef vector<PII> VPII;
const LL MOD=1000000007ll;
const LL INF=2000000000ll;

long power(long a,long b){return b?power(a*a%MOD,b/2)*(b%2?a:1)%MOD:1;}
long comb(int N,int K)
{
	long x=1,y=1;
	for(int i=1;i<=K;i++)
	{
		x=x*(N-i+1)%MOD;
		y=y*i%MOD;
	}
	return x*power(y,MOD-2)%MOD;
}

long hcomb(int N, int K){
    return comb(N+K-1, N-1);
}


int t = 1;
void dfs(const vector<vector<int> > &G, int v, vector<bool> &seen, VI &d, VI &f) {
    seen[v] = true; // v を訪問済にする
    d[v] = t;

    // v から行ける各頂点 next_v について
    for (auto next_v : G[v]) { 
        if (seen[next_v] == false) { // next_v が探索済だったらスルー
          	t++;
        	dfs(G, next_v, seen, d, f); // 再帰的に探索     	
        }
    }
    t++;
    f[v] = t;
}

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

    int n, u, k;
    cin >> n;
    VVI G(n);
    REP(i,n){
        cin >> u >> k;
        if (k){
            G[i].resize(k);
            REP(j,k) {
                cin >> G[i][j];
                G[i][j]--;
            }
          	sort(ALL(G[i]));
        }
    }

    vector<bool> seen(n,false);

    VI d(n,0), f(n,0);

  	REP(i,n){
      	if (d[i] == 0){
        	dfs(G, i, seen, d, f);
          	t++;
        }
    }

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

}

C++: #include <cstdio>

typedef struct Node {
    int id;
    int d;
    int f;
    int count;
} Node;


int count(int id, int now, Node *node, int a[101][100]){
    if(node[id].d == 0) node[id].d = now;
    else return now - 1;
    if(node[id].f != 0) return now - 1;
    for(int i = 0; i < node[id].count; ++i){
        int nextId = a[id][i];
        now = count(nextId, now + 1, node, a);
    }
    node[id].f = now + 1;
    return node[id].f;
}

int main(){
    int n;
    std::scanf("%d", &n);
    Node node[101];
    int a[101][100];
    for(int i = 1; i <= n; ++i){
        int id;
        std::scanf("%d", &id);
        node[id].id = id;
        std::scanf("%d", &node[id].count);
        for(int j = 0; j < node[id].count; ++j){
            std::scanf("%d", &a[id][j]);
        }
    }
    int now = 0;
    for(int i = 1; i <= n; ++i){
        now = count(i, now + 1, node, a);
    }
    for(int i = 1; i <= n; ++i){
        std::printf("%d %d %d\n", node[i].id, node[i].d, node[i].f);
    }

    return 0;
}

C++: #include <bits/stdc++.h>
using namespace std;
#define int long long
#define all(v) (v).begin(), (v).end()
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define endl '\n'
#define N 105
int n,cnt;
int graph[N][N];
int ans1[N],ans2[N];
bool checked[N];
void dfs(int i){
    if(checked[i]) return;
    checked[i] = true;
    ans1[i] = cnt++;
    for(int j = 0; j < n; j++){
        if(graph[i][j]) dfs(j);
    }
    ans2[i] = cnt++;
}

signed main() {
    cin >> n;
    for(int i = 0; i < n; i++){
        int a,b,c;
        cin >> a >> b;
        a--;
        for(int j = 0; j < b; j++){
            cin >> c;
            c--;
            graph[a][c] = 1;
        }
    }

    cnt = 1;
    for(int i = 0; i < n; i++){
        if(!checked[i]) dfs(i);
    }
    for(int i = 0; i < n; i++){
        cout << i + 1 << " " << ans1[i] << " " << ans2[i] << endl;
    }
}

C++: # include <iostream>
# include <vector>
# include <cstdio>
using namespace std;
struct info
{
	int st;
	int ed;
	bool vis = false;
};
int Time = 1;
vector<int> a[105];
info *ans;
void dfs(int i)
{
	ans[i].st = Time++;
	ans[i].vis = true;
	for(int j = 0;j < a[i].size();j++) if(!ans[a[i][j]].vis) dfs(a[i][j]);
	ans[i].ed = Time++;
}
int main(void)
{
	int n,cnt,k,u;
	scanf("%d",&n);
	ans = new info [n+1];
	for(int w = 0;w < n;w++)
	{
		scanf("%d %d",&u,&cnt);
		for(int i = 0;i < cnt;i++)
		{
			scanf("%d",&k);
			a[u].push_back(k);
		}
	}
	for(int i = 1;i <= n;i++) if(!ans[i].vis) dfs(i);
	for(int i = 1;i <= n;i++) printf("%d %d %d\n",i,ans[i].st,ans[i].ed);
	return 0;
}

C++: #include <bits/stdc++.h>
using namespace std;
vector<int> d;
vector<int> f;
vector<bool> used;
vector< vector<int> > v;
int n;
int cnt = 1;

void dfs(int pos){
  d[pos] = cnt; cnt++;
  used[pos] = true;
  for(int i = 0; i < n; i++){
    if(v[pos][i] && !used[i]){
      dfs(i);
    }
  }
  f[pos] = cnt; cnt++;
}

int main(void){
  ios::sync_with_stdio(false);
  cin.tie(0);

  cin >> n;
  d = f = vector<int>(n, -1);
  used = vector<bool>(n, false);
  v = vector< vector<int> >(n, vector<int>(n, 0));
  for(int i = 0; i < n; i++){
    int u, k; cin >> u >> k;
    u--;
    for(int j = 0; j < k; j++){
      int a; cin >> a;
      a--;
      v[u][a] = 1;
    }
  }
  for(int i = 0; i < n; i++)
    if(!used[i]) dfs(i);
  for(int i = 0; i < n; i++){
    cout << i+1 << " " << d[i] << " " << f[i] << endl;
  }

  return 0;
}

C++: //ノードが持つ情報→子のノードの情報
//ノードが子を持たないときはからのvectorを保持する
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>

using namespace std;

//このようにクラスやストラクトなどで一括で管理してしまえば良い
class Node{//普通の木用のデータ
//ノードはポインタでもっておこう
public:
  ///ノードの番号
  int num;
  //そのノードの親
  Node* p;
  //そのノードの子
  vector<Node*> v;
  //そのノードの発見時刻と完了時刻(これは分けて配列の方がいいかもしれない)
  //int d=0;int f=0;
};

//木じゃないのでrootがあるわけじゃない(重要)

//見つからなかったら-1
//非連結な場合もあるので
int find_next_root(vector<int> va){
  int l= int(va.size());
  for(int i=0;i<l;i++){
    if(va[i]==0){return i;}
  }
  return -1;
}


//Nは始まりのNode
//tはmainで管理しするので
void depth_search(Node& N,int& t,vector<int>& va,vector<int>& vf){
  //呼ばれた前の時点での時間が与えられる
  //子を全部見終わったら、そのノードも足し算してそこで終了する
  t++;
  va[N.num]=t;
  int l=N.v.size();
  for(int i=0;i<l;i++){
    if(va[N.v[i]->num]==0) depth_search(*(N.v[i]),t,va,vf);
  }
  t++;
  vf[N.num]=t;
}


int main(){
  int n;cin>>n;
  vector<Node> V(n);for(int i=0;i<n;i++){V[i].num=i;}
  vector<int> Va(n,0);vector<int> Vf(n,0);

  for(int i=0;i<n;i++){
    int k;cin >> k >> k;
    for(int j=0;j<k;j++){
      int l;cin >>l;l-=1;
      V[l].p=&V[i];
      V[i].v.push_back(&V[l]);
    }
  }
  //初めは一つのグラフだと思ってたけど非連結な場合もある
  int t=0;
  int x=0;
  while(x!=-1){
    depth_search(V[x],t,Va,Vf);
    x=find_next_root(Va);
  }
  for(int i=0;i<n;i++){
    cout << i+1 << " " << Va[i] << " " << Vf[i] << endl;
  }
}