Go: package main

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

func getScanner(fp *os.File) *bufio.Scanner {
	scanner := bufio.NewScanner(fp)
	scanner.Split(bufio.ScanWords)
	scanner.Buffer(make([]byte, 1000005), 1000005)
	return scanner
}

func getNextString(scanner *bufio.Scanner) string {
	scanner.Scan()
	return scanner.Text()
}

func getNextInt(scanner *bufio.Scanner) int {
	i, _ := strconv.Atoi(getNextString(scanner))
	return i
}

func getNextInt64(scanner *bufio.Scanner) int64 {
	i, _ := strconv.ParseInt(getNextString(scanner), 10, 64)
	return i
}

func getNextUint64(scanner *bufio.Scanner) uint64 {
	i, _ := strconv.ParseUint(getNextString(scanner), 10, 64)
	return i
}

func getNextFloat64(scanner *bufio.Scanner) float64 {
	i, _ := strconv.ParseFloat(getNextString(scanner), 64)
	return i
}

func main() {
	fp := os.Stdin
	wfp := os.Stdout
	cnt := 0

	if os.Getenv("MASPY") == "ますピ" {
		fp, _ = os.Open(os.Getenv("BEET_THE_HARMONY_OF_PERFECT"))
		cnt = 1
	}
	if os.Getenv("MASPYPY") == "ますピッピ" {
		wfp, _ = os.Create(os.Getenv("NGTKANA_IS_GENIUS10"))
	}

	scanner := getScanner(fp)
	writer := bufio.NewWriter(wfp)
	solve(scanner, writer)
	for i := 0; i < cnt; i++ {
		fmt.Fprintln(writer, "-----------------------------------")
		solve(scanner, writer)
	}
	writer.Flush()
}

func solve(scanner *bufio.Scanner, writer *bufio.Writer) {
	n := getNextInt(scanner)
	g := graph{}
	g.ee = make([]Edges, n)
	g.visited = make([]bool, n)
	g.found = make([]int, n)
	g.leave = make([]int, n)
	for i := 0; i < n; i++ {
		u := getNextInt(scanner) - 1
		k := getNextInt(scanner)
		for j := 0; j < k; j++ {
			v := getNextInt(scanner) - 1
			g.ee[u] = append(g.ee[u], Edge{
				to: v,
				id: u,
			})
		}
	}
	for i := 0; i < n; i++ {
		sort.Sort(g.ee[i])
	}
	for i := 0; i < n; i++ {
		if g.visited[i] {
			continue
		}
		g.dfs(i)
	}

	for i := 0; i < n; i++ {
		fmt.Fprintln(writer, fmt.Sprintf("%d %d %d", i+1, g.found[i], g.leave[i]))
	}
}

// Edge ...
type Edge struct {
	to, id int
}

type Edges []Edge

func (a Edges) Len() int           { return len(a) }
func (a Edges) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a Edges) Less(i, j int) bool { return a[i].id < a[j].id }

type graph struct {
	ee           []Edges
	t            int
	n            int
	visited      []bool
	found, leave []int
}

func (g *graph) dfs(i int) {
	g.visited[i] = true
	g.t++
	g.found[i] = g.t
	for _, e := range g.ee[i] {
		if g.visited[e.to] {
			continue
		}
		g.dfs(e.to)
	}
	g.t++
	g.leave[i] = g.t
}

C++: //#define _GLIBCXX_DEBUG

#include"bits/stdc++.h"
using namespace std;

//loops
#define REP(i,n) for(int i=0;i<(ll)(n);i++)
#define REPD(i,n) for(int i=(ll)(n)-1;i>=0;i--)
#define OneToN(i,n) for(int i=1;i<(ll)(n+1);i++)
#define ZeroToN(i,n) for(int i=0;i<(ll)(n+1);i++)
#define AToB(i,a,b) for(int i=a;i<(ll)(b+1);i++)
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define FORD(i,a,b) for(int i=(a);i>=(b);i--)

//bitsearch
#define BITSEARCH(bit, n) for (int bit = 0; bit < (1<<n); ++bit)
#define isOne(bit, i) bit & (1<<i)


//arrays
#define ALL(x) (x).begin(),(x).end() //sortなどの引数を省略したい
#define SIZE(x) ((ll)(x).size()) //sizeをsize_tからllに直しておく
#define add push_back


#define remove_unique(v) v.erase(unique(ALL(v)), v.end())

//pair
#define fir first
#define sec second

//types
#define ll long long
#define vec vector
#define cord2d pair<int,int>
//UNCOMMENT WHEN NEEDED
//#define int ll

//input
template <class T> T get(){ T s; cin >> s; return(s);}
#define gi get<int>()
#define gs get<string>()

template <class T> vector<T> getv(int n) { vec<T> v(n); REP(i, n) cin >> v[i]; return v; }
#define gim(n) getv<int>(n)
#define gsm(n) getv<string>(n)

//output
void print(int a){ cout << a << endl; }
void print(long long a){ cout << a << endl; }
void print(string a){ cout << a << endl; }
void print(char a){ cout << a << endl; }
void print(double a){ cout << a << endl; }
void print(double a, int dig){ cout << std::setprecision(dig) << a << endl; }

template <class T>ostream &operator<<(ostream &o,const vector<T>&v)
{o<<"{";for(int i=0;i<(int)v.size();i++)o<<(i>0?", ":"")<<v[i];o<<"}";return o;}

template <class T = int>
void print(vec<T> v){ cout << v << endl; }

template <class T = int>
void print2dVec(vec<vec<T>> v2d){for(vec<T> v: v2d) {print(v);}}

void YesNo1(bool i = true){ return print(i?"Yes":"No"); }
void YESNO2(bool i = true){ return print(i?"YES":"NO"); }

//debug output
#define var_name(var) #var
template <class T> void debug(T &var, int nest = 0, string before = "") { cout << string("          ", nest) << before; print(var); }

//name replacement
#define y0 y0__
#define y1 y1__
#define j0 j0__
#define j1 j1__

//bool lambdas
#define lamb(exp) [](auto i){return exp;}
#define isEven [](int i){return i % 2 == 0;}
#define isOdd [](int i){return i % 2 != 0;}

//constants
const ll INF = 1e18;
const int MOD = 10000007;

//maths
int factorial(int k){ int sum = 1; for (int i = 1; i <= k; ++i) { sum *= i; } return sum; }
int gcd(int a, int b) {
    if (b == 0)
        return a;
    return gcd(b, a % b);
}

////////////////////

// 深さ優先探索

void dfs(vec<vec<int>> &G, int v, int &count, vec<pair<int,int>> &verts, vec<bool> &seen) {
    seen[v] = true;
    int enterCount = count;
    count++;

    // v から行ける各頂点 next_v について
    for (auto next_v : G[v]) {
        if (!seen[next_v]) {
            dfs(G, next_v, count, verts, seen); // 再帰的に探索
            count++;
        }
    }
    verts[v] = make_pair(enterCount, count);
}

void Main() {
    //code here
    int n = gi;
    vec<vec<int>> graph(n);
    vec<pair<int,int>> verts(n);
    vec<bool> seen(n);
    REP(i, n) {
        int z = gi;
        int nextAmount = gi;
        vec<int> nexts;
        REP(j, nextAmount) {
            nexts.add(gi - 1);
        }
        graph[i] = nexts;
    }

    int count = 1;
    while(find(ALL(seen), false) != seen.end()) {
        //int target = (int) (find(ALL(seen), false) - seen.begin());
        dfs(graph, (int) (find(ALL(seen), false) - seen.begin()), count, verts, seen);
        count ++;
    }
    int i = 1;
    for (pair<int,int> v: verts) {
        cout << i << " " << v.first << " " << v.second << endl;
        i++;
    }
}

signed main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    Main();
}

C++: #include <iostream>
#include <cstring>
#include <cstdlib>
#include <sstream>
#include <stack>
#include <queue>
#include <list>
#include <stdio.h>
#include <algorithm>
#define MAX 100000
using namespace std;
typedef long long ll;
int map[255][255];
int f[255],d[255],time;
bool vis[255];
void dfs(int n,int u)
{
	if(vis[u]) return;
	vis[u]=true;
	d[u]=++time;
	for(int i=1;i<=n;i++)
	{
		if(map[u][i]) dfs(n,i);
	}
	f[u]=++time;
	return; 
}
int main()
{	
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			map[i][j]=0;
		}
	for(int i=0;i<n;i++)
	{
		int cur,k,u;
		cin>>cur>>k;
		while(k--)
		{
			cin>>u;
			map[cur][u]=1;
		}
	}
	for(int i=1;i<=n;i++)
	{
		vis[i]=false;
	}
	dfs(n,1);
	for(int i=1;i<=n;i++)
	{
		cout<<i<<" "<<d[i]<<" "<<f[i]<<endl;
	}
	return 0;
}

C++: #include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
#include <string>
#include <queue>

using namespace std;

int d[101];
int f[101];
bool isUsed[101];
int cnt = 0;

void DFS(vector< vector<int> >& G, int vertex)
{
	d[vertex] = ++cnt;
	for (int i = 0; i < G[vertex].size(); ++i)
	{
		if (G[vertex][i] == 1 && isUsed[i] == false)
		{
			isUsed[i] = true;
			DFS(G, i);
		}
	}
	f[vertex] = ++cnt;
}

void solve()
{
	int n;
	cin >> n;
	vector< vector<int> > G(n, vector<int>(n));
	for (int i = 0; i < n; ++i)
	{
		int u, k;
		cin >> u >> k;
		--u;
		for (int j = 0; j < k; ++j)
		{
			int v;
			cin >> v;
			--v;
			G[u][v] = 1;
		}
	}
	for (int i = 0; i < 101; ++i)
	{
		isUsed[i] = false;
	}
	isUsed[0] = true;
	DFS(G, 0);
	for (int i = 0; i < n; ++i)
	{
		cout << i + 1 << " " << d[i] << " " << f[i] << endl;
	}
}

int main()
{
	solve();
	return(0);
}