Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

Depth First Search

Depth-first search (DFS) follows the strategy to search ”deeper” in the graph whenever possible. In DFS, edges are recursively explored out of the most recently discovered vertex $v$ that still has unexplored edges leaving it. When all of $v$'s edges have been explored, the search ”backtracks” to explore edges leaving the vertex from which $v$ was discovered.

This process continues until all the vertices that are reachable from the original source vertex have been discovered. If any undiscovered vertices remain, then one of them is selected as a new source and the search is repeated from that source.

DFS timestamps each vertex as follows:

$d[v]$ records when $v$ is first discovered.

$f[v]$ records when the search finishes examining $v$’s adjacency list.

Write a program which reads a directed graph $G = (V, E)$ and demonstrates DFS on the graph based on the following rules:

$G$ is given in an adjacency-list. Vertices are identified by IDs $1, 2,... n$ respectively.

IDs in the adjacency list are arranged in ascending order.

The program should report the discover time and the finish time for each vertex.

When there are several candidates to visit during DFS, the algorithm should select the vertex with the smallest ID.

The timestamp starts with 1.

Input

In the first line, an integer $n$ denoting the number of vertices of $G$ is given. In the next $n$ lines, adjacency lists of $u$ are given in the following format:

$u$ $k$ $v_1$ $v_2$ ... $v_k$

$u$ is ID of the vertex and $k$ denotes its degree. $v_i$ are IDs of vertices adjacent to $u$.

Output

For each vertex, print $id$, $d$ and $f$ separated by a space character in a line. $id$ is ID of the vertex, $d$ and $f$ is the discover time and the finish time respectively. Print in order of vertex IDs.

Constraints

$1 \leq n \leq 100$

Sample Input 1

4
1 1 2
2 1 4
3 0
4 1 3

Sample Output 1

1 1 8
2 2 7
3 4 5
4 3 6

Sample Input 2

6
1 2 2 3
2 2 3 4
3 1 5
4 1 6
5 1 6
6 0

Sample Output 2

1 1 12
2 2 11
3 3 8
4 9 10
5 4 7
6 5 6

This is example for Sample Input 2 (discover/finish)

Reference

Introduction to Algorithms, Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. The MIT Press.

Solution in JAVA (judge verdict: Accepted)

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

class FastScanner {
    private final InputStream in = System.in;
    private final byte[] buffer = new byte[1024];
    private int ptr = 0;
    private int buflen = 0;

    private boolean hasNextByte() {
        if (ptr < buflen) {
            return true;
        } else {
            ptr = 0;
            try {
                buflen = in.read(buffer);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (buflen <= 0) {
                return false;
            }
        }
        return true;
    }

    private int readByte() {
        if (hasNextByte())
            return buffer[ptr++];
        else
            return -1;
    }

    private static boolean isPrintableChar(int c) {
        return 33 <= c && c <= 126;
    }

    public boolean hasNext() {
        while (hasNextByte() && !isPrintableChar(buffer[ptr]))
            ptr++;
        return hasNextByte();
    }

    public String next() {
        if (!hasNext())
            throw new NoSuchElementException();
        StringBuilder sb = new StringBuilder();
        int b = readByte();
        while (isPrintableChar(b)) {
            sb.appendCodePoint(b);
            b = readByte();
        }
        return sb.toString();
    }

    public long nextLong() {
        if (!hasNext())
            throw new NoSuchElementException();
        long n = 0;
        boolean minus = false;
        int b = readByte();
        if (b == '-') {
            minus = true;
            b = readByte();
        }
        if (b < '0' || '9' < b) {
            throw new NumberFormatException();
        }
        while (true) {
            if ('0' <= b && b <= '9') {
                n *= 10;
                n += b - '0';
            } else if (b == -1 || !isPrintableChar(b)) {
                return minus ? -n : n;
            } else {
                throw new NumberFormatException();
            }
            b = readByte();
        }
    }

    public int nextInt() {
        long nl = nextLong();
        if (nl < Integer.MIN_VALUE || nl > Integer.MAX_VALUE)
            throw new NumberFormatException();
        return (int) nl;
    }

    public double nextDouble() {
        return Double.parseDouble(next());
    }
}

public class Main {
    static int MOD = 1000000007;

    static int C(int n, int r) {
        int num = 1;
        for (int i = 1; i <= r; i++) {
            num = num * (n - i + 1) / i;
        }
        return num;
    }

    public static int gcd(int a, int b) {
        return b == 0 ? a : gcd(b, a % b);
    }

    static long lcm(long a, long b) {
        long temp;
        long c = a;
        c *= b;
        while ((temp = a % b) != 0) {
            a = b;
            b = temp;
        }
        return (long) (c / b);
    }

    static long f(long n, Map<Long, Long> map) {
        if (n < 2)
            return 1;
        else if (map.containsKey(n)) {
            return map.get(n);
        } else {
            map.put(n, n * f(n - 2, map));
            return map.get(n);
        }

    }

    public static long factorial(long n) {
        return n <= 0 ? 1 : n * factorial(n - 1);
    }

    private static long calcLCM(long val1, long val2) {
        long maxValue = Math.max(val1, val2);
        long minValue = Math.min(val1, val2);
        long val3 = maxValue * minValue;

        if (minValue == 0)
            return maxValue;

        long temp;
        while ((temp = maxValue % minValue) != 0) {
            maxValue = minValue;
            minValue = temp;
        }

        return (long) (val3 / minValue);
    }

    static String nextPermutation(String s) {
        ArrayList<Character> list = new ArrayList<>();
        for (int i = 0; i < s.length(); i++)
            list.add(s.charAt(i));

        int pivotPos = -1;
        char pivot = 0;
        for (int i = list.size() - 2; i >= 0; i--) {
            if (list.get(i) < list.get(i + 1)) {
                pivotPos = i;
                pivot = list.get(i);
                break;
            }
        }

        if (pivotPos == -1 && pivot == 0)
            return "Final";

        int L = pivotPos + 1, R = list.size() - 1;
        int minPos = -1;
        char min = Character.MAX_VALUE;
        for (int i = R; i >= L; i--) {
            if (pivot < list.get(i)) {
                if (list.get(i) < min) {
                    min = list.get(i);
                    minPos = i;
                }
            }
        }

        Collections.swap(list, pivotPos, minPos);
        Collections.sort(list.subList(L, R + 1));

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < list.size(); i++)
            sb.append(list.get(i));

        return sb.toString();

    }

    public List<String> permutation(List<String> list, String target, String ans) {
        if (target.length() <= 1) {
            list.add(ans + target);
        } else {
            for (int i = 0; i < target.length(); i++) {
                permutation(list, target.substring(0, i) + target.substring(i + 1), ans + target.charAt(i));
            }
        }
        return list;
    }

    // 探索したいkey以上の一番左のイテレータを返す
    public static int lowerBound(int[] a, int key) {
        if (a[a.length - 1] < key)
            return a.length;
        int lb = 0, ub = a.length - 1, mid;
        do {
            mid = (ub + lb) / 2;
            if (a[mid] < key)
                lb = mid + 1;
            else
                ub = mid;
        } while (lb < ub);
        return ub;
    }

    // 探索したいkeyより大きい一番左のイテレータを返す
    public static int upperBound(int[] a, int key) {
        if (a[a.length - 1] <= key)
            return a.length;
        int lb = 0, ub = a.length - 1, mid;
        do {
            mid = (ub + lb) / 2;
            if (a[mid] <= key)
                lb = mid + 1;
            else
                ub = mid;
        } while (lb < ub);
        return ub;
    }

    public static int count(int[] a, int key) {
        return upperBound(a, key) - lowerBound(a, key);
    }

    public static void quickSort(List<Long> data, int left, int right) {
        if (left >= right)
            return;

        int l = left;
        int r = right;
        long p = data.get((left + right) / 2);
        while (l <= r) {
            while (data.get(l) > p) {
                l++;
            }

            while (data.get(r) < p) {
                r--;
            }
            if (l <= r) {
                long temp = data.get(r);
                data.set(r, data.get(l));
                data.set(l, temp);
                l++;
                r--;
            }
        }

        quickSort(data, left, r);
        quickSort(data, l, right);

        return;

    }

    public static final int gcds(final int[] param) {
        final int len = param.length;
        int g = gcd(param[0], param[1]); // gcd(a, b) は以前作ったもの
        for (int i = 1; i < len - 1; i++) {
            g = gcd(g, param[i + 1]); // gcd(a, b) は以前作ったもの
        }
        return g;
    }

    static int n;
    static int[] d;
    static int[] f;
    static int ts = 0;
    static boolean visited[];

    static void dfs(int v, List<List<Integer>> array) {
        if (!visited[v]) {
            visited[v] = true;
            d[v] = ++ts;
        }
        for (int next_v : array.get(v)) {
            if (visited[next_v])
                continue;
            dfs(next_v, array);
        }
        if(f[v]==0)
        f[v] = ++ts;
    }

    public static void main(String[] args) {
        FastScanner fs = new FastScanner();
        n = fs.nextInt();
        List<List<Integer>> array = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            array.add(new ArrayList<>());
        }
        d = new int[n];
        f = new int[n];
        visited = new boolean[n];
        for (int j = 0; j < n; j++) {
            int u = fs.nextInt() - 1;
            int k = fs.nextInt();
            for (int i = 0; i < k; i++) {
                List<Integer> list = array.get(u);
                list.add(fs.nextInt() - 1);

            }
        }
        // for (List<Integer> list : array) {
        //     for (int i : list) {
        //         System.out.print(i);
        //     }
        //     System.out.println();
        // }
        for (int i = 0; i < n; i++) {
            dfs(i, array);
        }
        for (int v = 0; v < n; v++) {
            System.out.println(v + 1 + " " + d[v] + " " + f[v]);
        }
    }

}