#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5e4 + 4;
tuple<int, int, int, int> v[MAXN];
set<tuple<int, int> > d2;
int p[MAXN][3];
int n;
void process() {
  set<tuple<int, int> > d1;
  tuple<int, int> rest = {-1, -1};
  while (d2.size()) {
    int cur = get<0>(*d2.begin());
    while (d2.size() && get<0>(*d2.begin()) == cur) {
      int id = get<1>(*d2.begin());
      d1.insert({p[id][2], id});
      d2.erase(d2.begin());
    }
    for (auto mit = next(d1.begin()); mit != d1.end(); mit++) {
      auto mit2 = prev(mit);
      if (get<0>(*mit) == get<0>(*mit2)) {
        auto a = (*mit);
        auto b = (*mit2);
        printf("%d %d\n", get<1>(a), get<1>(b));
        d1.erase(a);
        d1.erase(b);
        break;
      }
    }
    if (get<0>(rest) != -1) d1.insert(rest), rest = {-1, -1};
    if (d1.empty()) continue;
    for (auto mit = next(d1.begin()); mit != d1.end(); mit++) {
      auto mit2 = prev(mit);
      if (get<0>(*mit) == get<0>(*mit2)) {
        auto a = (*mit);
        auto b = (*mit2);
        printf("%d %d\n", get<1>(a), get<1>(b));
        d1.erase(a);
        d1.erase(b);
        break;
      }
    }
    while (d1.size() > 1) {
      int a = get<1>(*d1.begin());
      d1.erase(d1.begin());
      int b = get<1>(*d1.begin());
      d1.erase(d1.begin());
      printf("%d %d\n", a, b);
    }
    if (d1.size()) rest = (*d1.begin()), d1.clear();
  }
  if (get<0>(rest) != -1) {
    int id = get<1>(rest);
    d2.insert({p[id][1], id});
  }
}
int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    scanf("%d %d %d", &p[i][0], &p[i][1], &p[i][2]);
    v[i] = {p[i][0], p[i][1], p[i][2], i};
  }
  sort(v + 1, v + 1 + n);
  for (int i = 1; i <= n; i++) {
    d2.insert({get<1>(v[i]), get<3>(v[i])});
    while (i + 1 <= n && get<0>(v[i + 1]) == get<0>(v[i])) {
      int id = get<3>(v[i + 1]);
      d2.insert({p[id][1], id});
      i++;
    }
    process();
  }
}
