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

#define TRACE(x) x
#define WATCH(x) TRACE(cout << #x" = " << x << endl)
#define WATCHR(a, b) TRACE(for (auto it=a; it!=b;) cout << *(it++) << " "; cout << endl)
#define WATCHC(V) TRACE({cout << #V" = "; WATCHR(V.begin(), V.end());})

#define all(x) (x).begin(), (x).end()

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vb = vector<bool>;
using vs = vector<string>;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<typename F> struct dinic {
    static constexpr F inf = numeric_limits<F>::has_infinity ?
        numeric_limits<F>::infinity() : numeric_limits<F>::max();

    int V;
    vvi adj;
    vi dest;
    vector<F> cap;

    dinic (int V = 0) : V(V) {
        adj.resize(V);
    }

    void __arc(int u, int v, F c) {
        adj[u].push_back(dest.size());
        dest.push_back(v);
        cap.push_back(c);
    }

    // Inserts a directed edge u --> v with capacity c.
    void arc(int u, int v, F c) {
        __arc(u, v, c);
        __arc(v, u, F(0));
    }

    bool bfs(int s, int t, vi& level, vector<F>& flow) const {
        level = vi(V, -1);
        level[s] = 0;
        for (queue<int> q({s}); !q.empty(); q.pop()) {
            int u = q.front();
            for (int e : adj[u]) {
                if (level[dest[e]] == -1 && flow[e] < cap[e]) {
                    level[dest[e]] = level[u] + 1;
                    q.push(dest[e]);
                }
            }
        }
        return level[t] != -1;
    }

    F augment(int s, int t, vi& level, vector<F>& flow, vi& inx, F cur) const {
        if (s == t) return cur;
        for (int e; inx[s] < adj[s].size(); inx[s]++) {
            e = adj[s][inx[s]];
            if (level[dest[e]] != level[s] + 1) continue;
            if (flow[e] == cap[e]) continue;
            F incr = augment(dest[e], t, level, flow, inx, min(cur, cap[e] - flow[e]));
            if (incr > F(0)) {
                flow[e] += incr;
                flow[e^1] -= incr;
                return incr;
            }
        }
        return F(0);
    }

    /*
     * Computes a maximum flow from node s to node t.
     *
     * Runs in O(V^2 * E) in the general case.
     * Runs in O(min{ V^(2/3), E^(1/2) } * E) if all edges have unit capacity.
     * Runs in O(V^(1/2) * E) for bipartite matching.
     */
    tuple<F, vector<F>> max_flow(int s, int t) const {
        assert(s != t);
        F res(0);
        vector<F> flow(cap.size());
        for (vi level; bfs(s, t, level, flow); ) {
            for (vi inx(V, 0); F incr = augment(s, t, level, flow, inx, inf); )
                res += incr;
        }
        return make_tuple(res, flow);
    }
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int N;
    cin >> N;

    int src = 0, snk = 2 * N;
    dinic<int> g(2 * N + 1);

    for (int i = 1; i <= N; i++) {
        g.arc(src, i, 1);
    }

    for (int i = 1; i <= N - 1; i++) {
        g.arc(N + i, snk, 1);

        int sz, w;
        cin >> sz;
        for (int j = 0; j < sz; j++) {
            cin >> w;
            g.arc(w, N + i, 1);
        }
    }

    auto max_flow = g.max_flow(src, snk);
    int F = get<0>(max_flow);
    vi flow = get<1>(max_flow);

    if (F != N - 1) {
        cout << -1 << endl;
        return 0;
    }

    int root;
    for (int e : g.adj[src]) {
        if (flow[e] == 0) {
            root = g.dest[e];
            break;
        }
    }

    vi vis(g.V);
    vis[src] = -1;
    vis[snk] = -1;
    vis[root] = root;

    queue<int> bfs({ root });
    while (!bfs.empty()) {
        int loc = bfs.front();
        bfs.pop();
        for (int e : g.adj[loc]) {
            if (!vis[g.dest[e]] && (loc > N && flow[e] == -1 || loc <= N && flow[e] == 0)) {
                vis[g.dest[e]] = loc;
                bfs.push(g.dest[e]);
            }
        }
    }

    vector<pair<int, int>> res(N - 1);
    for (int i = 1; i <= N; i++) {
        if (i == root) continue;
        if (!vis[i]) { cout << -1 << endl; return 0; }
        res[vis[i] - N - 1] = { i, vis[vis[i]] };
    }

    for (auto e : res) {
        cout << e.first << " " << e.second << "\n";
    }

    return 0;
}



Measured memory_bytes: 28316