Go: package main

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

var scanner = bufio.NewScanner(os.Stdin)

func nextString() string {
	scanner.Scan()
	return scanner.Text()
}

func nextInt() int {
	n, err := strconv.Atoi(nextString())
	if err != nil {
		fmt.Printf("strconv.Atoi failed: %v\n", err)
	}
	return n
}

var infty = 1000000000

type nodeKey int

type DirectedGraphNode struct {
	key       nodeKey
	adjacents []*DirectedGraphNode
	visited   bool
	d         int
	f         int
	parent    *DirectedGraphNode
}

type DirectedGraph struct {
	nodes []*DirectedGraphNode
}

func (g *DirectedGraph) setAdjacency(l, r nodeKey) {
	g.nodes[l].adjacents = append(g.nodes[l].adjacents, g.nodes[r])
}

func newDirectedGraph(nNodes int) *DirectedGraph {
	graph := DirectedGraph{}
	graph.nodes = make([]*DirectedGraphNode, nNodes)
	for iNodes := 0; iNodes < nNodes; iNodes++ {
		graph.nodes[iNodes] = &DirectedGraphNode{}
		graph.nodes[iNodes].key = nodeKey(iNodes)

		graph.nodes[iNodes].visited = false
		graph.nodes[iNodes].d = infty
		graph.nodes[iNodes].f = infty
		graph.nodes[iNodes].parent = nil
	}
	return &graph
}

type QueueEntry struct {
	node *DirectedGraphNode
	next *QueueEntry
}

type Queue struct {
	head, tail *QueueEntry
}

func (q *Queue) enqueue(node *DirectedGraphNode) {
	entry := QueueEntry{}
	entry.node = node

	if q.tail == nil {
		q.head = &entry
		q.tail = &entry
		return
	}

	q.tail.next = &entry
	q.tail = q.tail.next
}

func (q *Queue) dequeue() *QueueEntry {
	if q.head == nil {
		return nil
	}
	poped := q.head
	q.head = q.head.next
	if q.head == nil {
		q.tail = nil
	}
	return poped
}

func (q *Queue) isEmpty() bool {
	return q.head == nil
}

var time = 0

func fillDistancesByDepthFirstSearch(g *DirectedGraph) {
	for _, u := range g.nodes[1:] {
		if !u.visited {
			dfsVisit(g, u)
		}
	}
}

func dfsVisit(g *DirectedGraph, u *DirectedGraphNode) {
	time++
	u.d = time

	u.visited = true
	for _, v := range u.adjacents {
		if !v.visited {
			v.parent = u
			dfsVisit(g, v)
		}
	}
	time++
	u.f = time
}

func main() {
	scanner.Split(bufio.ScanWords)

	nNodes := nextInt()
	g := newDirectedGraph(nNodes + 1)

	for iNode := 0; iNode < nNodes; iNode++ {
		nodeKeyLeft := nodeKey(nextInt())
		nAdj := nextInt()
		for iAdj := 0; iAdj < nAdj; iAdj++ {
			nodeKeyRight := nodeKey(nextInt())
			g.setAdjacency(nodeKeyLeft, nodeKeyRight)
		}
	}

	fillDistancesByDepthFirstSearch(g)

	for iNode := 1; iNode <= nNodes; iNode++ {
		node := g.nodes[iNode]
		fmt.Println(node.key, node.d, node.f)
	}
}

C++: #include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <string>
#include <vector>

// Acknowledgement: Special thanks to kyomukyomupurin, who developed this
// template.
template <class T, class U>
std::ostream& operator<<(std::ostream& os, const std::pair<T, U>& p) {
  return os << '(' << p.first << ", " << p.second << ')';
}

template <class T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& vec) {
  int n = 0;
  for (auto e : vec) os << (n++ ? ", " : "{") << e;
  return os << (n ? "}" : "{}");
}

template <class T>
std::ostream& operator<<(std::ostream& os, const std::set<T>& st) {
  int n = 0;
  for (auto e : st) os << (n++ ? ", " : "{") << e;
  return os << (n ? "}" : "{}");
}

template <class T, class U>
std::ostream& operator<<(std::ostream& os, const std::map<T, U>& mp) {
  int n = 0;
  for (auto e : mp) os << (n++ ? ", " : "{") << e;
  return os << (n ? "}" : "{}");
}

template <class T>
std::istream& operator>>(std::istream& is, std::vector<T>& vec) {
  for (T& e : vec) is >> e;
  return is;
}

#ifdef LOCAL
#define debug(...) \
  std::cerr << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__)
#else
#define debug(...)
#endif

void debug_out() { std::cerr << '\n'; }

template <class Head, class... Tail>
void debug_out(Head&& head, Tail&&... tail) {
  std::cerr << head;
  if (sizeof...(Tail) != 0) std::cerr << ", ";
  debug_out(std::forward<Tail>(tail)...);
}
using namespace std;
using int64 = long long;

void dfs(int start_node_id, vector<vector<int>>& g, vector<int>& visit_status,
         vector<int>& first_visit, vector<int>& last_visit, int& time) {
  visit_status[start_node_id] = 1;
  first_visit[start_node_id] = ++time;
  for (int i = 0; i < (int)g[start_node_id].size(); i++) {
    int node_id = g[start_node_id][i];
    if (visit_status[node_id] == 0) {
      dfs(node_id, g, visit_status, first_visit, last_visit, time);
    }
  }
  visit_status[start_node_id] = 2;
  last_visit[start_node_id] = ++time;
}

int main() {
  int n, k, u, v;
  cin >> n;
  vector<vector<int>> g(n);
  for (int i = 0; i < n; i++) {
    cin >> u >> k;
    for (int j = 0; j < k; j++) {
      cin >> v;
      g[i].push_back(v - 1);
      // g[v - 1].push_back(i);
    }
  }
  debug(g);
  vector<int> visit_status(n), first_visit(n), last_visit(n);
  int time = 0;
  for (int i = 0; i < n; i++) {
    if (visit_status[i] == 0) {
      dfs(i, g, visit_status, first_visit, last_visit, time);
    }
  }
  for (int i = 0; i < n; i++) {
    cout << i + 1 << ' ' << first_visit[i] << ' ' << last_visit[i] << endl;
  }
  return 0;
}

C++: #include <stdio.h>
#include <iostream>
using namespace std;

class Adjl{
private:
	int u;
	int k;
	Adjl **next;
protected:
	int d;
	int f;
public:
	Adjl(int u);
	~Adjl(){ if (next != NULL) delete[] next; }
	void setV(int k, Adjl *vk);
	int depthFirstSearch(int time);
	void print();
};

Adjl::Adjl(int u)
{
	this->u = u;
	k = 0;
	next = NULL;
	d = 0;
	f = 0;
}

void Adjl::setV(int k, Adjl *vk)
{
	if (this->k == 0) next = new Adjl*[k];
	next[this->k] = vk;
	(this->k)++;
}

int Adjl::depthFirstSearch(int time)
{
	time++;
	d = time;
	for (int i = 0; i < k; i++) {
		if (next[i]->d == 0) {
			time = next[i]->depthFirstSearch(time);
		}
	}
	time++;
	f = time;
	return time;
}

void Adjl::print()
{
	cout << u << " " << d << " " << f << endl;
}

int main(void)
{
	int n;
	cin >> n;
	
	Adjl **adjl = new Adjl*[n];
	int check[100] = {0};

	for (int i = 0; i < n; i++) {
		int u, k;
		cin >> u >> k;
		if (check[i] == 0) {
			adjl[i] = new Adjl(u);
			check[i] = 1;
		}
		check[i] = 1;
		for (int j = 0; j < k; j++) {
			int vk;
			cin >> vk;
			if (check[vk - 1] == 0) {
				adjl[vk - 1] = new Adjl(vk);
				check[vk - 1] = 1;
			}
			adjl[i]->setV(k, adjl[vk - 1]);
		}
	}

	adjl[0]->depthFirstSearch(0);

	for (int i = 0; i < n; i++) {
		adjl[i]->print();
	}

	delete[] adjl;
	return 0;
}

C++: #if 0

#endif

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;

vector<int> E[110];
int N;
int D[110], F[110];

int tim = 0;
void dfs(int cur) {
  tim += 1;
  D[cur] = tim;
  for (auto it = E[cur].begin(); it != E[cur].end(); ++it) {
    if (D[*it] == 0) {
      dfs(*it);
    }
  }
  tim += 1;
  F[cur] = tim;
}

int main() {
  cin >> N;
  int c, k;
  for (int i = 1; i <= N; i++) {
    cin >> c >> k;
    int t;
    for (int j = 0; j < k; j++) {
      cin >> t; E[i].push_back(t);
    }
  }
  for (int i = 1; i <= N; i++) {
    D[i] = 0; F[i] = 0;
  }
  for (int i = 1; i <= N; i++) {
    if (D[i] == 0)
      dfs(i);
  }
  for (int i = 1; i <= N; i++)
    cout << i << ' ' << D[i] << ' ' << F[i] << endl;
}

C++: #include<bits/stdc++.h>

using namespace std;
#define fordebug int hoge;cin>>hoge;
#define DEKAI 1000000007
#define INF (1<<28)
#define lp(i,n) for(int i=0;i<n;i++)
#define lps(i,j,n) for(int i=j;i<n;i++)
#define floot10 cout<<fixed<<setprecision(10);
#define int long long
vector<int> v[200];
int n,t=1;
int d[200],f[200],done[200];

void dfs(int now){
	//cout<<now<<endl;
	d[now]=t;
	t++;
	done[now]=1;
	lp(i,v[now].size()){
		if(done[v[now][i]]==1) continue;
		dfs(v[now][i]);
	}
	f[now]=t;
	t++;
}

signed main(){
	cin>>n;
	lp(z,n){
		int i,c;
		cin>>i>>c;
		lp(j,c){
			int hoge;
			cin>>hoge;
			v[i].push_back(hoge);
		}
	}
	lps(i,1,n+1){
		if(done[i]==0)
			dfs(i);
	}
	lps(i,1,1+n){
		cout<<i<<" "<<d[i]<<" "<<f[i]<<endl;
	}
	return 0;
}