#include<vector>
#include<cmath>
#include<map>
#include<cstdlib>
#include<iostream>
#include<sstream>
#include<fstream>
#include<string>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<set>
#include<stack>
#include<bitset>
#include<functional>
#include<ctime>
#include<queue>
#include<deque>
#include<complex>
#include<cassert>
using namespace std;
#define pb push_back
#define pf push_front
typedef long long lint;
typedef complex<double> P;
#define mp make_pair
#define fi first
#define se second
typedef pair<int,int> pint;
#define All(s) s.begin(),s.end()
#define rAll(s) s.rbegin(),s.rend()
#define REP(i,a,b) for(int i=a;i<b;i++)
#define rep(i,n) REP(i,0,n)
//問題文および制約はちゃんと確認しよう！
//サイズは10^5じゃなくて2×10^5とかかもしれないし、重要な制約・条件を見落としているかも
//とりあえずサンプルを読んでから解法を考えよう？
//maxvの値に注意
const int maxv = 512810;
 
struct e_t { int to, cap, rev; };
 
int V, S, T;
vector<e_t> adj[maxv];
 
int rej[maxv];
int red[maxv];
 
void aedge(int i, int j, int c) {
  adj[i].pb((e_t){j, c, adj[j].size() + (i == j ? 1 : 0)});
  adj[j].pb((e_t){i, 0, adj[i].size() - 1});
}
 
void bfs() {
  memset(rej, -1, sizeof(rej));
  queue<int> q;
  rej[S] = 0;
  q.push(S);
  while (!q.empty()) {
    int v = q.front();
    q.pop();
    for (int i = 0; i < (int)adj[v].size(); i++) {
      e_t &e = adj[v][i];
      if (e.cap <= 0) continue;
      if (rej[e.to] != -1) continue;
      q.push(e.to);
      rej[e.to] = rej[v] + 1;
    }
  }
}
 
int dfs(int v, int flow) {
  if (v == T || flow == 0) return flow;
  for (; red[v] < (int)adj[v].size(); red[v]++) {
    e_t &e = adj[v][red[v]];
    if (rej[e.to] <= rej[v]) continue;
    int f = dfs(e.to, min(flow, e.cap));
    if (f > 0) {
      e.cap -= f;
      adj[e.to][e.rev].cap += f;
      return f;
    }
  }
  return 0;
}
 
int mflow() {
  int total = 0, fsum, f;
  do {
    bfs();
    memset(red, 0, sizeof(red));
    fsum = 0;
    while (0 < (f = dfs(S, 1001001001))) fsum += f;
    total += fsum;
  } while (fsum > 0);
  return total;
}

int p[200100];//マッチング相手
bool used[200100];//マッチング相手として使われたか
vector<pint> gr[200100];
pint out[200100];
void dfs(int v){
	//if(used[v]) return;
	used[v]=true;
	rep(i,gr[v].size()){
		if(used[gr[v][i].fi]) continue;
		out[gr[v][i].se]=mp(v,gr[v][i].fi);
		dfs(gr[v][i].fi);
	}
}
vector<int> v[200100];
int main()
{
	int n,a,b;
	cin>>n;
	rep(i,n-1){
		cin>>a;
		rep(j,a){
			cin>>b;b--;v[i].pb(b);
		}
	}
	S=2*n-1;T=S+1;V=T+1;
	//MF::init(V);
	rep(i,n-1){
		aedge(S,i,1);
		rep(j,v[i].size()) aedge(i,n-1+v[i][j],1);
		aedge(n-1+i,T,1);
	}
	//MF::solve(S,T);
	//cout<<MF::tof<<endl;
	if(mflow()<n-1){
		cout<<-1<<endl;
		return 0;
	}
	memset(p,-1,sizeof(p));
	/*rep(i,MF::m){
		if(MF::from[i]<n-1 && MF::to[i]>=n-1 && MF::to[i]<S && MF::capa[i]<1) p[MF::from[i]]=MF::to[i]-n+1;
	}*/
	rep(i,n-1) rep(j,adj[i].size()){
		if(adj[i][j].to<S && adj[i][j].to>=n-1 && adj[i][j].cap<1) p[i]=adj[i][j].to-n+1;
	}
	//rep(i,n-1) cout<<p[i]<<endl;
	memset(used,false,sizeof(used));
	rep(i,n-1) used[p[i]]=true;
	int root=-1;
	rep(i,n){
		if(!used[i]) root=i;
	}
	rep(i,n-1) rep(j,v[i].size()){
		if(v[i][j]==p[i]) continue;
		gr[v[i][j]].pb(mp(p[i],i));
	}
	memset(used,false,sizeof(used));
	dfs(root);
	rep(i,n){
		if(!used[i]){
			cout<<-1<<endl;return 0;
		}
	}
	rep(i,n-1){
		cout<<out[i].fi+1<<' '<<out[i].se+1<<endl;
	}
}


Measured memory_bytes: 55016