#include <bits/stdc++.h>
using namespace std;
string to_string(string s) { return "\"" + s + "\""; }
template <typename T>
string to_string(vector<T>);
template <typename U, typename V>
string to_string(pair<U, V> p) {
  return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";
}
template <typename T>
string to_string(vector<T> v) {
  string r = "[";
  for (int i = 0; i < (int)v.size() - 1; i++) r += to_string(v[i]) + ", ";
  if (!v.empty()) r += to_string(v.back());
  return r + "]";
}
const int N = 50010;
int n, x[N], y[N], z[N];
bool cmp(int i, int j) { return y[i] < y[j]; }
vector<pair<int, int> > res;
vector<int> layerFinal;
void processPts(vector<int>& v) {
  while ((int)v.size() > 1) {
    int a = v.back();
    v.pop_back();
    int b = v.back();
    v.pop_back();
    res.push_back({a, b});
  }
}
void processLine(vector<int>& line, vector<int>& lineFinal) {
  ;
  sort(begin(line), end(line), cmp);
  processPts(line);
  if (!line.empty()) lineFinal.push_back(line[0]);
}
void processLayer(const vector<int>& layer) {
  ;
  map<int, vector<int> > lines;
  for (int p : layer) lines[x[p]].push_back(p);
  vector<int> lineFinal;
  for (auto it = lines.begin(); it != lines.end(); it++)
    processLine(it->second, lineFinal);
  processPts(lineFinal);
  if (!lineFinal.empty()) layerFinal.push_back(lineFinal[0]);
}
void process() {
  map<int, vector<int> > layers;
  for (int i = 1; i <= n; i++) layers[z[i]].push_back(i);
  for (auto it = layers.begin(); it != layers.end(); it++)
    processLayer(it->second);
  processPts(layerFinal);
}
int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) scanf("%d%d%d", &x[i], &y[i], &z[i]);
  process();
  for (int i = 0; i < (int)res.size(); i++)
    printf("%d %d\n", res[i].first, res[i].second);
}
