#include <bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
using vi = vector<int>;
using ll = long long;
const int MAXN = 2e3 + 10;
struct point {
  vi p;
  int ind;
  point(int x, int y, int z) { p = {x, y, z}; }
  point() { p = vi(3); }
  bool operator<(const point& o) const { return p < o.p; }
  bool equalPref(const point& o, int d) const {
    for (int i = int(0); i < int(min(3, d)); i++)
      if (p[i] != o.p[i]) return false;
    return true;
  }
};
ostream& operator<<(ostream& os, const point& p) {
  os << p.ind;
  return os;
}
istream& operator>>(istream& is, point& p) {
  is >> p.p[0] >> p.p[1] >> p.p[2];
  return is;
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  int n;
  cin >> n;
  vector<point> ps(n);
  for (int i = int(0); i < int(n); i++) {
    cin >> ps[i];
    ps[i].ind = i + 1;
  }
  sort(ps.begin(), ps.end());
  forward_list<point> remaining(ps.begin(), ps.end());
  for (int d = int(3 - 1); d >= int(0); d--) {
    auto prev = remaining.before_begin(), act = prev;
    act++;
    while (act != remaining.end()) {
      auto nxt = act;
      nxt++;
      if (nxt != remaining.end() && act->equalPref(*nxt, d)) {
        cout << *act << " " << *nxt << '\n';
        act = remaining.erase_after(prev, ++nxt);
      } else {
        prev = act;
        act = nxt;
      }
    }
  }
  return 0;
}
