#include <bits/stdc++.h>
using namespace std;
const int INF = (int)1e9;
struct Point {
  int x, y, z;
  int idx;
  bool operator<(const Point& o) const {
    if (x != o.x) {
      return x < o.x;
    }
    if (y != o.y) {
      return y < o.y;
    }
    return z < o.z;
  }
};
const int N = 50050;
bool mark[N];
vector<pair<int, int>> ans;
void matchUnmarked(const vector<Point>& points) {
  int prevIdx = -1;
  for (int i = 0; i < points.size(); ++i) {
    if (mark[points[i].idx]) {
      continue;
    }
    if (prevIdx != -1) {
      ans.push_back({points[prevIdx].idx, points[i].idx});
      mark[points[prevIdx].idx] = mark[points[i].idx] = true;
      prevIdx = -1;
    } else {
      prevIdx = i;
    }
  }
}
void solve(vector<Point>& points) {
  sort(points.begin(), points.end(), [&](const Point& a, const Point& b) {
    if (a.y != b.y) {
      return a.y < b.y;
    }
    return a.z < b.z;
  });
  for (int i = 0; i < points.size();) {
    vector<Point> cur;
    int j = i;
    while (j < points.size() and points[i].y == points[j].y) {
      cur.push_back(points[j]);
      ++j;
    }
    i = j;
    for (int k = 0; k + 1 < cur.size(); k += 2) {
      ans.push_back({cur[k].idx, cur[k + 1].idx});
      mark[cur[k].idx] = mark[cur[k + 1].idx] = true;
    }
  }
  matchUnmarked(points);
}
int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  memset(mark, false, sizeof(mark));
  int n;
  cin >> n;
  vector<Point> points(n);
  for (int i = 0; i < n; ++i) {
    cin >> points[i].x >> points[i].y >> points[i].z;
    points[i].idx = i;
  }
  sort(points.begin(), points.end());
  for (int i = 0; i < n;) {
    vector<Point> cur;
    int j = i;
    while (j < points.size() and points[i].x == points[j].x) {
      cur.push_back(points[j]);
      ++j;
    }
    i = j;
    solve(cur);
  }
  matchUnmarked(points);
  for (int i = 0; i < n; ++i) {
    assert(mark[i]);
  }
  for (int i = 0; i < ans.size(); ++i) {
    cout << ans[i].first + 1 << " " << ans[i].second + 1 << "\n";
  }
  assert(ans.size() * 2 == n);
  return 0;
}
