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++: #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;
}

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

using namespace std;

int d[10010]={0};//最初に訪問した時刻を入れるタイムスタンプ
int f[10010]={0};//vの隣接リストを調べ終えた時刻のタイムスタンプ

int back[10010]={0};//番号nのもときた街番号をいれる

int g[1001][1001]={{0}};//要素を0で初期化
int timestamp = 1;
int number;
int dfs(int cur){
  d[cur]= timestamp;
  timestamp += 1;
  for(int dst = 0; dst < number; dst++){
    if(g[cur][dst]==1 && d[dst]==0){
      // d[dst]=timestamp;
      back[dst]=cur;
      dfs(dst);
    }
  }
  // timestamp += 1;
  // cout << "f[cur]" <<f[cur]<<"timestamp"<<timestamp<<"cur"<<cur<<endl;
  // abort();
  if(timestamp > number*2){
    return 0;
  }
  // cout << "f[cur]" <<f[cur]<<"timestamp"<<timestamp<<"cur"<<cur<<endl;
  // abort();
  f[cur]=timestamp;
  timestamp++;
}
void ans(int n){
  for (int i = 0; i < n; ++i)
  {
    cout << i+1 << " " << d[i] <<" "<< f[i] <<endl;
  }
}

int main(){
  // int g[1001][1001]={{0}};//要素を0で初期化
  int n,a,b,c;
  cin >> n;
  number = n;
  for (int i = 0; i < n; ++i)
  {
    cin >> a >> b ;
    for (int i = 0; i < b; ++i)
    {
      cin >> c;
      g[a-1][c-1]=1;/* code */
    }
  }
  d[0]=1;
  dfs(0);
  for (int i = 0; i < number; ++i)
  {
    if(d[i]==0){
      d[i]=timestamp;
      dfs(i);
    }
  }
  // for (int i = 0; i < number; ++i)
  // {
  //   cout << back[i]<<" ";
  // }

  ans(n);
}