#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
typedef pair<int,P> P1;
typedef pair<P,P> P2;
#define pu push
#define pb push_back
#define mp make_pair
#define eps 1e-7
#define INF 1000000000
#define mod 1000000007
#define fi first
#define sc second
#define rep(i,x) for(int i=0;i<x;i++)
#define repn(i,x) for(int i=1;i<=x;i++)
#define SORT(x) sort(x.begin(),x.end())
#define ERASE(x) x.erase(unique(x.begin(),x.end()),x.end())
#define POSL(x,v) (lower_bound(x.begin(),x.end(),v)-x.begin())
#define POSU(x,v) (upper_bound(x.begin(),x.end(),v)-x.begin())

vector<int>ans[200005];
int beg = -1;
bool in[400005];

//O(E sqrt V)

struct HopcroftKarp {
  vector< vector< int > > graph, rev;
  vector< int > dist, match, revmatch;
  vector< bool > used, vv;

  HopcroftKarp(int n, int m) : graph(n), rev(m), match(m, -1), revmatch(n,-1), used(n) {}

  void add_edge(int u, int v) {
    graph[u].push_back(v);
    rev[v].push_back(u);
  }

  void bfs() {
    dist.assign(graph.size(), -1);
    queue< int > que;
    for(int i = 0; i < graph.size(); i++) {
      if(!used[i]) {
        que.emplace(i);
        dist[i] = 0;
      }
    }

    while(!que.empty()) {
      int a = que.front();
      que.pop();
      for(auto &b : graph[a]) {
        int c = match[b];
        if(c >= 0 && dist[c] == -1) {
          dist[c] = dist[a] + 1;
          que.emplace(c);
        }
      }
    }
  }

  bool dfs(int a) {
    vv[a] = true;
    for(auto &b : graph[a]) {
      int c = match[b];
      if(c < 0 || (!vv[c] && dist[c] == dist[a] + 1 && dfs(c))) {
        match[b] = a;
        revmatch[a] = b;
        used[a] = true;
        return (true);
      }
    }
    return (false);
  }

  int bipartite_matching() {
    int ret = 0;
    while(true) {
      bfs();
      vv.assign(graph.size(), false);
      int flow = 0;
      for(int i = 0; i < graph.size(); i++) {
        if(!used[i] && dfs(i)) ++flow;
      }
      if(flow == 0) return (ret);
      ret += flow;
    }
  }

  void output() {
    for(int i = 0; i < match.size(); i++) {
      if(~match[i]) {
        cout << match[i] << "-" << i << endl;
      }
    }
  }
  
  void put_ans() {
    for(int i = 0; i < match.size(); i++) {
      if(~match[i]) {
        ans[match[i]].pb(i);
      }
      else{
      	beg = i;
      }
    }
  }
  
  void DFS(int cur,bool right){
      //cout << cur << " " << right << endl;
  	if(right){
  		
  		for(int i=0;i<rev[cur].size();i++){
  			if(match[cur] == rev[cur][i]) continue;
  			int to = rev[cur][i];
  			//cout << to << endl;
  			if(in[to]) continue;
  			in[to] = 1;
  			ans[to].pb(cur);
  			//cout << to << " " << cur << endl;
  			//cout << to << endl;
  			DFS(to,false);
  		}
  	}
  	else{
  		
		for(int i=0;i<graph[cur].size();i++){
  			if(match[graph[cur][i]] != cur) continue;
  			int to = graph[cur][i];
  			if(in[to+200001]) continue;
  			in[to+200001] = 1;
  			DFS(to,true);
  		}
  	}
  }
};

int n;
vector<int>vec[200005];

int par[200005],ran[200005];
void init(){ for(int i=0;i<200005;i++) par[i] = i; }
int find(int x){ if(x == par[x]) return x; else return par[x] = find(par[x]); }
void unite(int x,int y){
	x = find(x); y = find(y); if(x==y) return;
	if(ran[x] < ran[y]) par[x] = y;
	else{
		par[y] = x;
		if(ran[x] == ran[y]) ran[x]++;
	}
}
bool same(int x,int y){ return find(x)==find(y); }


int main(){
	scanf("%d",&n);
	HopcroftKarp bm(n-1,n);
	for(int i=0;i<n-1;i++){
		int a; scanf("%d",&a);
		rep(j,a){
			int x; scanf("%d",&x); x--;
			vec[i].pb(x);
			bm.add_edge(i,x);
		}
	}
	int v = bm.bipartite_matching(); 
	bm.put_ans();
	if(beg != -1) bm.DFS(beg,true);
	init();
	for(int i=0;i<n-1;i++){
		if(ans[i].size() != 2){
			puts("-1"); return 0;
		}
		int x = ans[i][0], y = ans[i][1];
		if(same(x,y)){
			puts("-1"); return 0;
		}
		unite(x,y);
	}
//	puts("YES");
	rep(i,n-1) cout << ans[i][0]+1 << " " << ans[i][1]+1 << endl;
}

Measured memory_bytes: 35424