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.

Constraints

* $1 \leq n \leq 100$

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.

Examples

Input

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


Output

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


Input

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


Output

1 1 12
2 2 11
3 3 8
4 9 10
5 4 7
6 5 6
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.*;
public class Main {
	static long MOD=1000000007;
	static int[][] df;
	static int time=1;
	public static void main(String[] args){
		PrintWriter out = new PrintWriter(System.out);
		InputReader sc=new InputReader(System.in);
		int N=sc.nextInt();
		graph G=new graph(N);
		df=new int[N][2];
		for (int i = 0; i < N; i++) {
			int a=sc.nextInt()-1;
			int k=sc.nextInt();
			for (int j = 0; j < k; j++) {
				int b=sc.nextInt()-1;
				G.addEdge(a, b, 1);
			}
		}
		for (int i = 0; i < N; i++) {
			Collections.sort(G.list[i]);
		}
		for (int i = 0; i < N; i++) {
			if (df[i][0]!=0) {
				continue;
			}
			G.dfs(i);
			time++;
		}
		for (int i = 1; i <= N; i++) {
			out.println(i+" "+df[i-1][0]+" "+df[i-1][1]);
		}
		out.flush();
	}
	static class Edge implements Comparable<Edge>{
		int to;
		long v;
		public Edge(int to,long v) {
			this.to=to;
			this.v=v;
		}
		@Override
    	public boolean equals(Object obj) {
    		if(obj instanceof Edge) {
    			Edge other = (Edge) obj;
    			return other.to==this.to && other.v==this.v;
    		}
    		return false;
    	}//同値の定義
    	@Override
    	public int hashCode() {
    		return Objects.hash(this.to,this.v);
    	}
    	@Override
		  public int compareTo( Edge p2 ){
			 if (this.to>p2.to) {
				return 1;
			}
			 else if (this.to<p2.to) {
				return -1;
			}
			 else {
				return 0;
			}
		  }
	} 
	static class graph{
		ArrayList<Edge>[] list;
		int size;
		long INF=Long.MAX_VALUE/2;
		int[] color;
		@SuppressWarnings("unchecked")
		public graph(int n) {
			size = n;
			list = new ArrayList[n];
			color =new int[n];
			for(int i=0;i<n;i++){
				list[i] = new ArrayList<Edge>();
			}
		}
		void addEdge(int from,int to,long w) {
			list[from].add(new Edge(to,w));
		}
		void Treedfs(int v, int p ) {
			for (Edge nv : list[v]) {
				if (nv.to == p) continue;  
				//処理書く
				Treedfs(nv.to, v);
			}
		}
		void dfs(int v) {
			df[v][0]=time;
			for (Edge nv: list[v]) {
				if (df[nv.to][0]!=0) {
					continue;
				}
				time++;
				dfs(nv.to);
			}
			time++;
			df[v][1]=time;
		}
		boolean nibugraph(int s,int colors) {
			color[s]=colors;
			for (Edge nv: list[s]) {
				if (color[nv.to]==colors) {
					return false;
				}
				if (color[nv.to]==0&&!nibugraph(nv.to, -colors)) {
					return false;
				}
			}
			return true;
		}
		long[] bfs(int s) {
			long[] l=new long[size];
			for (int i = 0; i < l.length; i++) {
				l[i]=-1;
			}
			l[s]=0;
			ArrayDeque<Integer> qArrayDeque=new ArrayDeque<Integer>();
			qArrayDeque.add(s);
			while (!qArrayDeque.isEmpty()) {
				int v=qArrayDeque.poll();
				for (Edge nv: list[v]) {
					if (l[nv.to]==-1) {
						l[nv.to]=l[v]+nv.v;
						qArrayDeque.add(nv.to);
					}
				}
			}
			return l;
		}
		long[] bellman_ford(int sv,int gv) {
			long[] d=new long[size];
			Arrays.fill(d, INF);
			d[sv]=0;
			for (int i = 0; i < d.length; i++) {
				for (int j = 0; j < d.length; j++) {
					if (d[j]==INF) {
						continue;
					}
					for (Edge edge :list[j]) {
						if (d[edge.to]>d[j]+edge.v) {
							d[edge.to]=d[j]+edge.v;
						}
					}
				}
			}
			long ans=d[d.length-1];
			for (int i = 0; i < d.length; i++) {
				for (int j = 0; j < d.length; j++) {
					if (d[j]==INF) {
						continue;
					}
					for (Edge edge :list[j]) {
						if (d[edge.to]>d[j]+edge.v) {
							d[edge.to]=-INF;
						}
					}
				}
			}
			if (ans!=d[gv]) {
				return null;
			}//sv~gv間に負の閉路があれば検出
			return d;
		}
		long[] dijkstra(int s){
	        long[] L = new long[size];
	        for(int i=0;i<size;i++){
	            L[i] = -1;
	        }
	        int[] visited = new int[size];
	        L[s] = 0;
	        PriorityQueue<Edge> Q = new PriorityQueue<Edge>();
	        Q.add(new Edge(s,0L));//Edgeに最短距離候補を持たせる
	        while(!Q.isEmpty()){
	            Edge C = Q.poll();
	            if(visited[C.to]==0){
	                L[C.to] = C.v;
	                visited[C.to] = 1;
	                for(Edge D:list[C.to]){
	                    Q.add(new Edge(D.to,L[C.to]+D.v));
	                }
	            }
	        }
	        return L;
	    }
	    int[] topological(){
			int[] indegree = new int[size];//入次数
	        for (int i = 0; i < size; i++) {
	        	for (Edge edge: list[i]) {
					indegree[edge.to]++;
				}
	        }
	        ArrayDeque<Integer> q = new ArrayDeque<Integer>();
	        int[] count=new int[size];
	        for (int i = 0; i < size; i++) {
	            if (indegree[i] == 0) {
	                q.addLast(i);
	            }
	        }
	        int cnt = 0;
	        List<Integer> res = new ArrayList<Integer>();
	        while (!q.isEmpty()) {
	            // 接続先の頂点を探索開始
	            int u = q.removeLast();
	            res.add(u);
	            for (Edge edge : list[u]) {
	                indegree[edge.to]--;
	                count[edge.to]=Math.max(count[u]+1,count[edge.to]);
	                if (indegree[edge.to] == 0) {
	                    q.addFirst(edge.to);
	                }
	            }
	            cnt++;
	        }
	        if (cnt<size) {
				return null;
			}//閉路があればその頂点は一度もキューに入らない
	        return count;
	        //countはその頂点までの最大パス長
	        //resにcountの値が小さい順にソートされたものが入っている。
		}
	}
	static class InputReader { 
		private InputStream in;
		private byte[] buffer = new byte[1024];
		private int curbuf;
		private int lenbuf;
		public InputReader(InputStream in) {
			this.in = in;
			this.curbuf = this.lenbuf = 0;
		}
 
		public boolean hasNextByte() {
			if (curbuf >= lenbuf) {
				curbuf = 0;
				try {
					lenbuf = in.read(buffer);
				} catch (IOException e) {
					throw new InputMismatchException();
				}
				if (lenbuf <= 0)
					return false;
			}
			return true;
		}
 
		private int readByte() {
			if (hasNextByte())
				return buffer[curbuf++];
			else
				return -1;
		}
 
		private boolean isSpaceChar(int c) {
			return !(c >= 33 && c <= 126);
		}
 
		private void skip() {
			while (hasNextByte() && isSpaceChar(buffer[curbuf]))
				curbuf++;
		}
 
		public boolean hasNext() {
			skip();
			return hasNextByte();
		}
 
		public String next() {
			if (!hasNext())
				throw new NoSuchElementException();
			StringBuilder sb = new StringBuilder();
			int b = readByte();
			while (!isSpaceChar(b)) {
				sb.appendCodePoint(b);
				b = readByte();
			}
			return sb.toString();
		}
 
		public int nextInt() {
			if (!hasNext())
				throw new NoSuchElementException();
			int c = readByte();
			while (isSpaceChar(c))
				c = readByte();
			boolean minus = false;
			if (c == '-') {
				minus = true;
				c = readByte();
			}
			int res = 0;
			do {
				if (c < '0' || c > '9')
					throw new InputMismatchException();
				res = res * 10 + c - '0';
				c = readByte();
			} while (!isSpaceChar(c));
			return (minus) ? -res : res;
		}
 
		public long nextLong() {
			if (!hasNext())
				throw new NoSuchElementException();
			int c = readByte();
			while (isSpaceChar(c))
				c = readByte();
			boolean minus = false;
			if (c == '-') {
				minus = true;
				c = readByte();
			}
			long res = 0;
			do {
				if (c < '0' || c > '9')
					throw new InputMismatchException();
				res = res * 10 + c - '0';
				c = readByte();
			} while (!isSpaceChar(c));
			return (minus) ? -res : res;
		}
 
		public double nextDouble() {
			return Double.parseDouble(next());
		}
 
		public int[] nextIntArray(int n) {
			int[] a = new int[n];
			for (int i = 0; i < n; i++)
				a[i] = nextInt();
			return a;
		}
 
		public long[] nextLongArray(int n) {
			long[] a = new long[n];
			for (int i = 0; i < n; i++)
				a[i] = nextLong();
			return a;
		}
 
		public char[][] nextCharMap(int n, int m) {
			char[][] map = new char[n][m];
			for (int i = 0; i < n; i++)
				map[i] = next().toCharArray();
			return map;
		}
	}
}

