#include <bits/stdc++.h>
using namespace std;
template <typename T>
using vec = vector<T>;
template <typename T>
using pq = priority_queue<T, vector<T>, std::greater<T>>;
template <typename T>
using mxpq = priority_queue<T>;
template <typename A, typename B>
istream& operator>>(istream& s, pair<A, B>& p) {
  return s >> p.first >> p.second;
}
template <typename T>
istream& operator>>(istream& s, vec<T>& p) {
  for (T& t : p) s >> t;
  return s;
}
struct uf {
  vec<long long> v;
  uf(int n) : v(n) { iota(v.begin(), v.end(), 0); }
  int find(int a) {
    if (v[a] == a) return a;
    v[a] = find(v[a]);
    return v[a];
  }
  void u(int a, int b) {
    a = find(a), b = find(b);
    if (a == b) return;
    v[a] = b;
  }
};
int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  vec<long long> l0, r0;
  vec<pair<long long, long long>> p;
  uf u(n + 1);
  for (int i = (1); i < (n + 1); ++i) {
    int l, r;
    cin >> l >> r;
    p.emplace_back(l, r);
    if (l == 0)
      l0.push_back(i);
    else
      u.u(i, l);
    if (r == 0)
      r0.push_back(i);
    else
      u.u(i, r);
  }
  assert((int)(l0).size() == (int)(r0).size());
  while ((int)(l0).size() > 1) {
    int l = l0.back();
    l0.pop_back();
    int r = r0.back();
    if (u.find(l) == u.find(r))
      r = r0[(int)(r0).size() - 2], r0.erase(r0.end() - 2);
    else
      r0.pop_back();
    p[l - 1].first = r;
    p[r - 1].second = l;
    u.u(l, r);
  }
  for (auto& x : (p)) cout << x.first << " " << x.second << endl;
}
