Go: package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

type TimeRecord struct {
	discoverTime int
	finishTime   int
}

func main() {
	sc := bufio.NewScanner(os.Stdin)
	sc.Split(bufio.ScanWords)

	n, err := nextInt(sc)
	if err != nil {
		panic(err)
	}

	timeRecordMap := make([]TimeRecord, n)
	childrenMap := make([][]int, n)
	for i := 1; i <= n; i++ {
		node, err := nextInt(sc)
		if err != nil {
			panic(err)
		}
		timeRecordMap[node-1] = TimeRecord{-1, -1}

		childNum, err := nextInt(sc)
		if err != nil {
			panic(err)
		}
		childrenMap[node-1] = make([]int, childNum)
		for j := 0; j < childNum; j++ {
			child, err := nextInt(sc)
			if err != nil {
				panic(err)
			}
			childrenMap[node-1][j] = child
		}
	}

	timer := 1
	for {
		startNode := findStartNode(childrenMap, timeRecordMap)
		if startNode > 0 {
			visit(startNode, &timer, childrenMap, timeRecordMap)
		} else {
			break
		}
	}
	for k, v := range timeRecordMap {
		fmt.Printf("%d %d %d\n", k+1, v.discoverTime, v.finishTime)
	}
}

func findStartNode(childrenMap [][]int, timeRecordMap []TimeRecord) int {
	x := -1
	for node, timeRecord := range timeRecordMap {
		if timeRecord.discoverTime < 0 {
			x = node + 1
			break
		}
	}
	if x < 0 {
		return -1
	}

	found := false
	i := 0
	for i < len(childrenMap) {
		children := childrenMap[i]
		for j := 0; j < len(children); j++ {
			if children[j] == x {
				x = j + 1
				found = true
				break
			}
		}
		if found {
			i = 0
		} else {
			i++
		}
	}
	return x
}

func visit(currentNode int, timer *int, childrenMap [][]int, timeRecordMap []TimeRecord) {
	if timeRecordMap[currentNode-1].discoverTime > 0 {
		return
	}
	children := childrenMap[currentNode-1]
	timeRecordMap[currentNode-1] = TimeRecord{*timer, timeRecordMap[currentNode-1].finishTime}
	(*timer)++
	for i := 0; i < len(children); i++ {
		visit(children[i], timer, childrenMap, timeRecordMap)
	}
	timeRecordMap[currentNode-1] = TimeRecord{timeRecordMap[currentNode-1].discoverTime, *timer}
	(*timer)++
}

func nextInt(sc *bufio.Scanner) (int, error) {
	sc.Scan()
	return strconv.Atoi(sc.Text())
}

C++: #include<bits/stdc++.h>
using namespace std;

#define White 0 //未訪問
#define Gray 1 //訪問中、または未訪問へのEdgeがある
#define Black 2 //訪問済み

int n;
int Graph[100][100] = {};

int color[100] = {}, d[100], f[100], tt; 
// d[v] = v 訪問時の時間 f[v] = v の隣接リスト調査後の時間 tt = 時間の記録

int nt[100] = {};


//u に隣接する v を番号順に取得
int next(int u){
  for(int v = nt[u] ; v < n ; v++ ){
    nt[u] = v + 1;
    if(Graph[u][v] != 0) return v;
  }
  return -1;
}

void dfs_visit(int r){
  stack<int> S;
  S.push(r); //スタート地点をスタックに積む

  color[r] = Gray; //スタート地点を訪問済みに

  tt++;
  d[r] = tt; //時間を取得

  while(!S.empty()){
    int u = S.top();  //topの値を入れる
    int v = next(u);
    if(v != -1){
      //末訪問なら訪問中にし時間を取得、次の頂点をスタックに積む
      //どちらでもなければ、popでスタックから外し訪問済みにする
      if(color[v] == White){ 
	color[v] = Gray;
	tt++;
	d[v] = tt;
	S.push(v);
      }
    }else{
      S.pop();
      color[u] = Black;
      tt++;
      f[u] = tt;
    }
  }
}
  
void dfs(){
  tt = 0;

  for(int u = 0 ; u < n ; u++){
    if(color[u] == White) dfs_visit(u); //その頂点が未訪問なら
  }

  for(int i = 0 ; i < n ; i++){
    cout << i + 1 << " " << d[i] << " " << f[i] << endl;
  }
}

int main(){
  int u, k, v; //u = 頂点番号 k = 出次数 v = u に隣接する頂点番号

  cin >> n;
     
  for(int i = 0 ; i < n ; i++){
    cin >> u >> k; 
    u--;
    for(int j = 0 ; j < k ; j++){
      cin >> v; 
      v--;
      Graph[u][v] = 1;
    }
  }

  dfs();
     
  return 0;
}

C++: #define _USE_MATH_DEFINES
#include<iostream>
#include<algorithm>
#include<string>
#include<iomanip>
#include<cmath>
#include<vector>
#include<deque>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define vec vector
#define vll vec<ll>
#define dou(s) atof(s.c_str())
#define lon(s) atoll(s.c_str())
#define str(n) to_string(n)
#define rep(i,a,b) for(ll i=a;i<b;i++)
#define rrep(i,b,a) for(ll i=b;i>=a;i--)
#define ALL(a) a.begin(),a.end()
#define rALL(a) a.rbegin(),a.rend()
#define line "\n"
#define mav(a) (*max_element(ALL(a)))
#define miv(a) (*min_element(ALL(a)))
#define vfind(a,n) (find(ALL(a),n)-a.begin())
#define prque priority_queue
#define MOD 1000000007ll
using namespace std;
void solve();
int main() {
	cin.tie(0);
	ios::sync_with_stdio(false);
	solve();
}

//nCk(mod p)
/*ll fac[510000], finv[510000], inv[510000];
void cmodf(ll p = MOD) {
	fac[0] = fac[1] = 1;
	finv[0] = finv[1] = 1;
	inv[1] = 1;
	rep(i, 2, 510000) {
		fac[i] = fac[i - 1] * i%p;
		inv[i] = p - inv[p%i] * (p / i) % p;
		finv[i] = finv[i - 1] * inv[i] % p;
	}
}
ll cmod(ll n, ll k, ll p = MOD) {
	return fac[n] * (finv[k] * finv[n - k] % p) % p;
}*/

//最大公約数
ll gcd(ll n, ll m) {
	ll k = n % m;
	while (k != 0) { n = m; m = k; k = n % m; }
	return m;
}
ll gcd(vector<ll> a) {
	rep(i, 1, a.size()) {
		ll k = a[i - 1] % a[i];
		while (k != 0) {
			a[i - 1] = a[i];
			a[i] = k;
			k = a[i - 1] % a[i];
		}
	}
	return a.back();
}

//最小公倍数
ll lcm(ll n, ll m) {
	ll t = n * m, k = n % m;
	while (k != 0) { n = m; m = k; k = n % m; }
	return t / m;
}
ll lcm(vector<ll> a) {
	ll n;
	rep(i, 1, a.size()) {
		n = a[i - 1] * a[i];
		ll k = a[i - 1] % a[i];
		while (k != 0) {
			a[i - 1] = a[i];
			a[i] = k;
			k = a[i - 1] % a[i];
		}
		n /= a[i];
		a[i] = n;
	}
	return n;
}

//keyの小さい方から探していった時に初めてvalueがjoken以上になる時のkey（keyが増えたらvalueも増えるか同じになるとき）
ll value(ll key) {
	/*変更*/
	return 0;
}
ll mini(ll lkey, ll rkey/*ここも調べる*/, ll joken) {
	ll mkey;
	while (lkey != rkey) {
		mkey = (lkey + rkey) / 2;
		if (value(mkey) >= joken)rkey = mkey;
		else lkey = mkey + 1;
	}
	return rkey;
}

//ペア配列の整理
struct cpl {
	ll f, s;
	bool operator<(const cpl &c) const {
		return f != c.f ? f < c.f : s < c.s;
	};
};

//変数定義

//再帰関数

//アルゴリズム
void solve() {
	ll n; cin >> n;
	vec<vll> a(n, vll(n, 0));
	rep(i, 0, n) {
		ll k, m; cin >> k >> m;
		rep(j, 0, m) {
			ll l; cin >> l;
			a[k - 1][l - 1] = 1;
		}
	}
	vll st(n, 0), en(n, 0);
	ll t = 0;
	while (miv(st) == 0) {
		ll k; t++;
		rep(i, 0, n)if (st[i] == 0) { k = i; break; }
		deque<ll> b(1, k);
		st[k] = t;
		rep(i, 0, n)a[i][k] = 0;
		while (b.size() != 0) {
			t++;
			bool x = true;
			rep(i, 0, n) {
				if (a[b[b.size() - 1]][i] == 1) {
					rep(j, 0, n)a[j][i] = 0;
					b.push_back(i);
					st[i] = t;
					x = false;
					break;
				}
			}
			if (x) {
				en[b[b.size() - 1]] = t;
				b.pop_back();
			}
		}
	}
	rep(i, 0, n) {
		cout << i + 1 << " " << st[i] << " " << en[i] << line;
	}
}

C++: #include<math.h>
#include<cmath>
#include<bitset>
#include<stdio.h>
#include<string>
#include<map>
#include<algorithm>
#include<vector>
#include<iostream>
#include<utility>
using namespace std;
vector<int> seen;
int d[110],f[110],z=1;
int dfs(vector<vector<int> > g,int v){
  int i;
  seen[v]=1;
  d[v]=z;
  z++;
  for(i=0;i<g[v].size();i++){
    if(seen[g[v][i]]==0){
      dfs(g,g[v][i]);
    }
  }
  f[v]=z;
  z++;
};
int main(){
  vector<vector<int> > g(110);
  int x,i,j,u,k,n,v;
  scanf("%d\n",&n);
  for(i=0;i<n;i++){
    scanf("%d %d",&u,&k);
    u--;
    for(j=0;j<k;j++){
      scanf("%d",&v);
      v--;
      g[u].push_back(v);
    }
  }
  seen.assign(n,0);
  for(i=0;i<n;i++){
    if(seen[i]==0){
      dfs(g,i);
    }
  }
  for(i=0;i<n;i++){
    printf("%d %d %d\n",i+1,d[i],f[i]);
  }
  return 0;
}