Problem:
Score : 2200 points

Problem Statement

You are given N-1 subsets of \{1,2,...,N\}. Let the i-th set be E_i.

Let us choose two distinct elements u_i and v_i from each set E_i, and consider a graph T with N vertices and N-1 edges, whose vertex set is \{1,2,..,N\} and whose edge set is (u_1,v_1),(u_2,v_2),...,(u_{N-1},v_{N-1}).
Determine if T can be a tree by properly deciding u_i and v_i.
If it can, additionally find one instance of (u_1,v_1),(u_2,v_2),...,(u_{N-1},v_{N-1}) such that T is actually a tree.

Constraints

2 \leq N \leq 10^5

E_i is a subset of \{1,2,..,N\}.

|E_i| \geq 2

The sum of |E_i| is at most 2 \times 10^5.

Input

Input is given from Standard Input in the following format:

N
c_1 w_{1,1} w_{1,2} ... w_{1,c_1}
:
c_{N-1} w_{N-1,1} w_{N-1,2} ... w_{N-1,c_{N-1}}

Here, c_i stands for the number of elements in E_i, and w_{i,1},...,w_{i,c_i} are the c_i elements in c_i.
Here, 2 \leq c_i \leq N, 1 \leq w_{i,j} \leq N, and w_{i,j} \neq w_{i,k} (1 \leq j < k \leq c_i) hold.

Output

If T cannot be a tree, print -1; otherwise, print the choices of (u_i,v_i) that satisfy the condition, in the following format:

u_1 v_1
:
u_{N-1} v_{N-1}

Sample Input 1

5
2 1 2
3 1 2 3
3 3 4 5
2 4 5

Sample Output 1

1 2
1 3
3 4
4 5

Sample Input 2

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

Sample Output 2

-1

Sample Input 3

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

Sample Output 3

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

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Time Limit Exceeded)

import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.StringTokenizer;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskF solver = new TaskF();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskF {
        static final int INF = (int) 1e9;

        void addEdge(TaskF.Vertex a, TaskF.Vertex b, int cap) {
            TaskF.Edge ab = new TaskF.Edge();
            TaskF.Edge ba = new TaskF.Edge();
            ab.rev = ba;
            ab.dest = b;
            ab.cap = cap;
            ba.dest = a;
            ba.rev = ab;
            a.outgo.add(ab);
            b.outgo.add(ba);
        }

        public void solve(int testNumber, InputReader in, PrintWriter out) {
            int n = in.nextInt();
            List<TaskF.Vertex> all = new ArrayList<>();
            TaskF.Vertex s = new TaskF.Vertex();
            TaskF.Vertex t = new TaskF.Vertex();
            all.add(s);
            all.add(t);
            TaskF.Vertex[] left = new TaskF.Vertex[n];
            for (int i = 0; i < n; ++i) {
                left[i] = new TaskF.Vertex();
                left[i].tag = i + 1;
                all.add(left[i]);
                addEdge(s, left[i], 1);
            }
            for (int i = 0; i < n - 1; ++i) {
                TaskF.Vertex cur = new TaskF.Vertex();
                all.add(cur);
                cur.tag = i;
                int count = in.nextInt();
                addEdge(cur, t, 1);
                for (int j = 0; j < count; ++j) {
                    addEdge(left[in.nextInt() - 1], cur, 1);
                }
            }
            int flow = maxFlowDinic(s, t, all.size());
            if (flow != n - 1) {
                out.println(-1);
                return;
            }
            for (TaskF.Vertex v : all) {
                v.generation = 0;
                v.nextEdge = 0;
            }
            flow = maxFlowDfs(s, t);
            if (flow != 0) {
                throw new RuntimeException();
            }
            for (TaskF.Vertex v : left) {
                if (v.generation != 1) {
                    out.println(-1);
                    return;
                }
            }
            String[] res = new String[n - 1];
            for (TaskF.Vertex v : left) {
                if (v.prevInDfs == s) continue;
                TaskF.Vertex u = v.prevInDfs.prevInDfs;
                res[v.prevInDfs.tag] = v.tag + " " + u.tag;
            }
            for (String x : res) {
                out.println(x);
            }
        }

        private int maxFlowDfs(TaskF.Vertex s, TaskF.Vertex t) {
            int curGen = 0;
            int res = 0;
            while (true) {
                ++curGen;
                int by = s.dfs(t, curGen, INF);
                if (by == 0) break;
                res += by;
            }
            return res;
        }

        boolean bfs(TaskF.Vertex s, TaskF.Vertex t, TaskF.Vertex[] q, int curGen) {
            int qh = 0, qt = 0;
            q[qt++] = s;
            s.generation = curGen;
            s.dist = 0;
            while (t.generation < curGen && qh < qt) {
                TaskF.Vertex v = q[qh++];
                for (TaskF.Edge e : v.outgo) {
                    if (e.dest.generation < curGen && e.flow < e.cap) {
                        e.dest.generation = curGen;
                        q[qt++] = e.dest;
                        e.dest.dist = v.dist + 1;
                    }
                }
            }
            return t.generation == curGen;
        }

        int maxFlowDinic(TaskF.Vertex s, TaskF.Vertex t, int nv) {
            int flow = 0;
            int curGen = 0;
            TaskF.Vertex[] q = new TaskF.Vertex[nv];
            for (; ; ) {
                ++curGen;
                if (!bfs(s, t, q, curGen)) break;
                ++curGen;
                t.markDinic(curGen - 1, curGen);
                while (true) {
                    int pushed = s.dfsDinic(t, curGen, INF);
                    if (pushed == 0) break;
                    flow += pushed;
                }
            }
            return flow;
        }

        static class Edge {
            TaskF.Vertex dest;
            TaskF.Edge rev;
            int cap = 0;
            int flow = 0;

        }

        static class Vertex {
            int tag = -1;
            int generation = 0;
            int interestingGeneration = 0;
            int nextEdge = 0;
            int dist;
            TaskF.Vertex prevInDfs;
            List<TaskF.Edge> outgo = new ArrayList<>();

            int dfsDinic(TaskF.Vertex t, int curGen, int got) {
                if (got == 0) return 0;
                if (this == t) return got;
                if (interestingGeneration != curGen) return 0;
                if (generation < curGen) {
                    generation = curGen;
                    nextEdge = 0;
                }
                for (; nextEdge < outgo.size(); ++nextEdge) {
                    TaskF.Edge e = outgo.get(nextEdge);
                    if (e.dest.dist != dist + 1) continue;
                    int pushed = e.dest.dfs(t, curGen, Math.min(got, e.cap - e.flow));
                    if (pushed > 0) {
                        e.flow += pushed;
                        e.rev.flow -= pushed;
                        return pushed;
                    }
                }
                return 0;
            }

            public int dfs(TaskF.Vertex t, int curGen, int max) {
                if (this == t) return max;
                generation = curGen;
                for (int i = 0; i < outgo.size(); ++i) {
                    TaskF.Edge e = outgo.get(nextEdge);
                    if (e.flow < e.cap && e.dest.generation < curGen) {
                        int nmax = Math.min(max, e.cap - e.flow);
                        if (nmax > 0) {
                            e.dest.prevInDfs = this;
                            nmax = e.dest.dfs(t, curGen, nmax);
                            if (nmax > 0) {
                                e.flow += nmax;
                                e.rev.flow -= nmax;
                                return nmax;
                            }
                        }
                    }
                    ++nextEdge;
                    if (nextEdge == outgo.size()) nextEdge = 0;
                }
                return 0;
            }

            public void markDinic(int bfsGen, int curGen) {
                interestingGeneration = curGen;
                for (TaskF.Edge e : outgo)
                    if (e.dest.interestingGeneration < curGen && e.dest.generation == bfsGen && e.dest.dist == dist - 1 && e.rev.flow < e.rev.cap) {
                        e.dest.markDinic(bfsGen, curGen);
                    }
            }

        }

    }

    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

    }
}