import java.util.*;
import java.util.stream.IntStream;
import java.io.*;

public class BalancedRemovalshard {

    /************************ SOLUTION STARTS HERE ************************/

    static int pairing[][];
    static int pts[][];
    static int ptr;
    static Integer order[];

    static int rec(int dim, int l, int r) {  // r excluded

        if(dim == 0) {  // base case

            for(int i = l; i + 1 < r; i += 2) {
                pairing[ptr][0] = order[i] + 1;
                pairing[ptr][1] = order[i + 1] + 1;
                ptr++;
            }

            return (r - l) % 2 == 0 ? -1 : r - 1;
        }
        else {

            ArrayList<Integer> collect = new ArrayList<>();
            for(int i = l; i < r; ) {
                int j = i + 1;
                for(; j < r && pts[j][dim] == pts[i][dim]; j++)
                    ;

                int ret = rec(dim - 1, i, j);
                i = j;
                if(ret >= 0)
                    collect.add(ret);
            }

            for(int i = 0; i + 1 < collect.size(); i += 2) {
                pairing[ptr][0] = order[collect.get(i)] + 1;
                pairing[ptr][1] = order[collect.get(i + 1)] + 1;
                ptr++;
            }

            return collect.size() % 2 == 0 ? -1 : collect.get(collect.size() - 1);
        }
    }

    private static void solve() {

        int n = nextInt();
        pts = new int[n][];

        for(int i = 0; i < n; i++)
            pts[i] = nextIntArray(3);

        order = new Integer[n];
        for(int i = 0; i < n; i++)
            order[i] = i;

        Arrays.sort(order, (i1, i2) -> pts[i1][2] != pts[i2][2] ? pts[i1][2] - pts[i2][2]
                                    : (pts[i1][1] != pts[i2][1] ? pts[i1][1] - pts[i2][1]
                                    :  pts[i1][0] - pts[i2][0]));

        Arrays.sort(pts, (p1, p2) -> p1[2] != p2[2] ? p1[2] - p2[2]
                                    : (p1[1] != p2[1] ? p1[1] - p2[1] : p1[0] - p2[0]));

        ptr = 0;
        pairing = new int[n / 2][2];

        rec(2, 0, n);

        Arrays.stream(pairing).forEach(p -> println(p[0] + " " + p[1]));

    }

    /************************ SOLUTION ENDS HERE ************************/

    /************************ TEMPLATE STARTS HERE **********************/

    public static void main(String[] args) throws IOException {
        reader = new BufferedReader(new InputStreamReader(System.in));
        writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)), false);
        st     = null;
        solve();
        reader.close();
        writer.close();
    }

    static BufferedReader reader;
    static PrintWriter    writer;
    static StringTokenizer st;

    static String next()
    {while(st == null || !st.hasMoreTokens()){try{String line = reader.readLine();if(line == null){return null;}
    st = new StringTokenizer(line);}catch (Exception e){throw new RuntimeException();}}return st.nextToken();}
    static String nextLine()  {String s=null;try{s=reader.readLine();}catch(IOException e){e.printStackTrace();}return s;}
    static int    nextInt()   {return Integer.parseInt(next());}
    static long   nextLong()  {return Long.parseLong(next());}
    static double nextDouble(){return Double.parseDouble(next());}
    static char   nextChar()  {return next().charAt(0);}
    static int[]  nextIntArray(int n)         {int[] a= new int[n];   int i=0;while(i<n){a[i++]=nextInt();}  return a;}
    static long[] nextLongArray(int n)        {long[]a= new long[n];  int i=0;while(i<n){a[i++]=nextLong();} return a;}
    static int[]  nextIntArrayOneBased(int n) {int[] a= new int[n+1]; int i=1;while(i<=n){a[i++]=nextInt();} return a;}
    static long[] nextLongArrayOneBased(int n){long[]a= new long[n+1];int i=1;while(i<=n){a[i++]=nextLong();}return a;}
    static void   print(Object o)  { writer.print(o);  }
    static void   println(Object o){ writer.println(o);}

    /************************ TEMPLATE ENDS HERE ************************/

}


import java.util.*;
import java.io.*;

public class BalancedRemovalsEasier {

    /************************ SOLUTION STARTS HERE ************************/

    static long dist(long p1[], long p2[]) {
        long sum = 0;

        for(int i = 0; i < 3; i++)
            sum += (p1[i] - p2[i]) * (p1[i] - p2[i]);

        return sum;
    }

    private static void solve() {

        int n = nextInt();
        long pts[][] = new long[n][];
        for(int i = 0; i < n; i++)
            pts[i] = nextLongArray(3);

        boolean marked[] = new boolean[n];
        for(int i = 0; i < n; i++)
            if(!marked[i]) {
                marked[i] = true;
                int nearest = -1;
                for(int j = i + 1; j < n; j++)
                    if(!marked[j]) {
                        nearest = j;
                        break;
                    }
//                println(i);
                for(int j = i + 1; j < n; j++) {
                    if(!marked[j] && dist(pts[i], pts[j]) < dist(pts[i], pts[nearest]))
                        nearest = j;
//                    println(dist(pts[i], pts[j]));
                }
                marked[nearest] = true;
                println((i + 1) + " " + (nearest + 1));
            }

    }

    /************************ SOLUTION ENDS HERE ************************/

    /************************ TEMPLATE STARTS HERE **********************/

    public static void main(String[] args) throws IOException {
        reader = new BufferedReader(new InputStreamReader(System.in));
        writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)), false);
        st     = null;
        solve();
        reader.close();
        writer.close();
    }

    static BufferedReader reader;
    static PrintWriter    writer;
    static StringTokenizer st;

    static String next()
    {while(st == null || !st.hasMoreTokens()){try{String line = reader.readLine();if(line == null){return null;}
    st = new StringTokenizer(line);}catch (Exception e){throw new RuntimeException();}}return st.nextToken();}
    static String nextLine()  {String s=null;try{s=reader.readLine();}catch(IOException e){e.printStackTrace();}return s;}
    static int    nextInt()   {return Integer.parseInt(next());}
    static long   nextLong()  {return Long.parseLong(next());}
    static double nextDouble(){return Double.parseDouble(next());}
    static char   nextChar()  {return next().charAt(0);}
    static int[]  nextIntArray(int n)         {int[] a= new int[n];   int i=0;while(i<n){a[i++]=nextInt();}  return a;}
    static long[] nextLongArray(int n)        {long[]a= new long[n];  int i=0;while(i<n){a[i++]=nextLong();} return a;}
    static int[]  nextIntArrayOneBased(int n) {int[] a= new int[n+1]; int i=1;while(i<=n){a[i++]=nextInt();} return a;}
    static long[] nextLongArrayOneBased(int n){long[]a= new long[n+1];int i=1;while(i<=n){a[i++]=nextLong();}return a;}
    static void   print(Object o)  { writer.print(o);  }
    static void   println(Object o){ writer.println(o);}

    /************************ TEMPLATE ENDS HERE ************************/

}


import java.util.*;
import java.util.stream.IntStream;
import java.io.*;

public class BalancedRemovalsEasier {

    /************************ SOLUTION STARTS HERE ************************/

    static long dist(long p1[], long p2[]) {
        long sum = 0;

        for(int i = 0; i < 3; i++)
            sum += (p1[i] - p2[i]) * (p1[i] - p2[i]);

        return sum;
    }

    private static void solve() {

        int n = nextInt();
        long pts[][] = new long[n][];
        for(int i = 0; i < n; i++)
            pts[i] = nextLongArray(3);

        boolean marked[] = new boolean[n];
        for(int i = 0; i < n; i++)
            if(!marked[i]) {
                marked[i] = true;
                int nearest = -1;
                for(int j = i + 1; j < n; j++)
                    if(!marked[j]) {
                        nearest = j;
                        break;
                    }

                for(int j = i + 1; j < n; j++) {
                    if(!marked[j] && dist(pts[i], pts[j]) < dist(pts[i], pts[nearest]))
                        nearest = j;
                }
                marked[nearest] = true;
                println((i + 1) + " " + (nearest + 1));
            }

    }

    private static void solveHard() {

        int n = nextInt();
        long pts[][] = new long[n][];
        for(int i = 0; i < n; i++)
            pts[i] = nextLongArray(3);

        Integer index[] = new Integer[n];
        for(int i = 0; i < n; i++)
            index[i] = i;

        long origin[] = new long[] {0, 0, 0};

        Arrays.sort(index, (i1, i2) -> Long.compare(dist(origin, pts[i1]), dist(origin, pts[i2])));

        for(int i = 0; i < n / 2; i += 2)
            println((index[i] + 1) + " " + (index[i + 1] + 1));
    }

    /************************ SOLUTION ENDS HERE ************************/

    /************************ TEMPLATE STARTS HERE **********************/

    public static void main(String[] args) throws IOException {
        reader = new BufferedReader(new InputStreamReader(System.in));
        writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)), false);
        st     = null;
        solveHard();
        reader.close();
        writer.close();
    }

    static BufferedReader reader;
    static PrintWriter    writer;
    static StringTokenizer st;

    static String next()
    {while(st == null || !st.hasMoreTokens()){try{String line = reader.readLine();if(line == null){return null;}
    st = new StringTokenizer(line);}catch (Exception e){throw new RuntimeException();}}return st.nextToken();}
    static String nextLine()  {String s=null;try{s=reader.readLine();}catch(IOException e){e.printStackTrace();}return s;}
    static int    nextInt()   {return Integer.parseInt(next());}
    static long   nextLong()  {return Long.parseLong(next());}
    static double nextDouble(){return Double.parseDouble(next());}
    static char   nextChar()  {return next().charAt(0);}
    static int[]  nextIntArray(int n)         {int[] a= new int[n];   int i=0;while(i<n){a[i++]=nextInt();}  return a;}
    static long[] nextLongArray(int n)        {long[]a= new long[n];  int i=0;while(i<n){a[i++]=nextLong();} return a;}
    static int[]  nextIntArrayOneBased(int n) {int[] a= new int[n+1]; int i=1;while(i<=n){a[i++]=nextInt();} return a;}
    static long[] nextLongArrayOneBased(int n){long[]a= new long[n+1];int i=1;while(i<=n){a[i++]=nextLong();}return a;}
    static void   print(Object o)  { writer.print(o);  }
    static void   println(Object o){ writer.println(o);}

    /************************ TEMPLATE ENDS HERE ************************/

}


import java.util.*;
import java.util.stream.IntStream;
import java.io.*;

public class BalancedRemovalsEasier {

    /************************ SOLUTION STARTS HERE ************************/

    static long dist(long p1[], long p2[]) {
        long sum = 0;

        for(int i = 0; i < 3; i++)
            sum += (p1[i] - p2[i]) * (p1[i] - p2[i]);

        return sum;
    }

    private static void solve() {

        int n = nextInt();
        long pts[][] = new long[n][];
        for(int i = 0; i < n; i++)
            pts[i] = nextLongArray(3);

        boolean marked[] = new boolean[n];
        for(int i = 0; i < n; i++)
            if(!marked[i]) {
                marked[i] = true;
                int nearest = -1;
                for(int j = i + 1; j < n; j++)
                    if(!marked[j]) {
                        nearest = j;
                        break;
                    }

                for(int j = i + 1; j < n; j++) {
                    if(!marked[j] && dist(pts[i], pts[j]) < dist(pts[i], pts[nearest]))
                        nearest = j;
                }
                marked[nearest] = true;
                println((i + 1) + " " + (nearest + 1));
            }

    }

    private static void solveHard() {

        int n = nextInt();
        long pts[][] = new long[n][];
        for(int i = 0; i < n; i++)
            pts[i] = nextLongArray(3);

        Integer index[] = new Integer[n];
        for(int i = 0; i < n; i++)
            index[i] = i;

        long origin[] = new long[] {0, 0, 0};

        Arrays.sort(index, (i1, i2) -> Long.compare(dist(origin, pts[i1]), dist(origin, pts[i2])));

        for(int i = 0; i < n; i += 2)
            println((index[i] + 1) + " " + (index[i + 1] + 1));
    }

    /************************ SOLUTION ENDS HERE ************************/

    /************************ TEMPLATE STARTS HERE **********************/

    public static void main(String[] args) throws IOException {
        reader = new BufferedReader(new InputStreamReader(System.in));
        writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)), false);
        st     = null;
        solveHard();
        reader.close();
        writer.close();
    }

    static BufferedReader reader;
    static PrintWriter    writer;
    static StringTokenizer st;

    static String next()
    {while(st == null || !st.hasMoreTokens()){try{String line = reader.readLine();if(line == null){return null;}
    st = new StringTokenizer(line);}catch (Exception e){throw new RuntimeException();}}return st.nextToken();}
    static String nextLine()  {String s=null;try{s=reader.readLine();}catch(IOException e){e.printStackTrace();}return s;}
    static int    nextInt()   {return Integer.parseInt(next());}
    static long   nextLong()  {return Long.parseLong(next());}
    static double nextDouble(){return Double.parseDouble(next());}
    static char   nextChar()  {return next().charAt(0);}
    static int[]  nextIntArray(int n)         {int[] a= new int[n];   int i=0;while(i<n){a[i++]=nextInt();}  return a;}
    static long[] nextLongArray(int n)        {long[]a= new long[n];  int i=0;while(i<n){a[i++]=nextLong();} return a;}
    static int[]  nextIntArrayOneBased(int n) {int[] a= new int[n+1]; int i=1;while(i<=n){a[i++]=nextInt();} return a;}
    static long[] nextLongArrayOneBased(int n){long[]a= new long[n+1];int i=1;while(i<=n){a[i++]=nextLong();}return a;}
    static void   print(Object o)  { writer.print(o);  }
    static void   println(Object o){ writer.println(o);}

    /************************ TEMPLATE ENDS HERE ************************/

}


import java.util.*;

public class ProblemF {

    public static void main (String[] strings){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        TreeMap<Integer, List<Triple>> map = new TreeMap<>();
        for (int i = 1; i <= n; i++) {
            int x = scanner.nextInt();
            Triple triple = new Triple(i,  scanner.nextInt(), scanner.nextInt());
            if (map.containsKey(x)){
                map.get(x).add(triple);
            }
            else{
                ArrayList<Triple> temp = new ArrayList<>();
                temp.add(triple);
                map.put(x, temp);
            }
        }

        List<Triple>[] arr = new List[map.size()];
        map.values().toArray(arr);

        Triple old = null;
        for (int i = 0; i < arr.length; i++) {
            List<Triple> list = arr[i];
            Collections.sort(list);
            int j = 0;
            boolean bool = list.size() %2 != 0;
            for (; j < list.size() - 1;) {
                Triple curr = list.get(j);
                if (curr.y == list.get(j+1).y){
                    System.out.println(curr.n + " " + list.get(j+1).n);
                    j+=2;
                }
                else{
                    int pos = j+1;
                    int nextY = list.get(pos).y;
                    int nextEl = pos;
                    int delta = Integer.MAX_VALUE;
                    while (pos < list.size() && list.get(pos).y == nextY){
                        int currDelta = Math.abs(curr.z - list.get(pos).z);
                        if (currDelta < delta){
                            delta = currDelta;
                            nextEl = pos;
                        }
                        pos++;
                    }
                    System.out.println(curr.n + " " + list.remove(nextEl).n);
                    j++;
                }
            }
            if (bool){
                if (old == null){
                    old = list.get(list.size()-1);

                }
                else{
                    System.out.println(old.n + " " + list.get(list.size()-1).n);
                    old = null;
                }
            }
        }

    }
}

class Triple implements Comparable<Triple>{
    int n, y, z;

    public Triple(int n,  int y, int z) {
        this.n = n;

        this.y = y;
        this.z = z;
    }

    @Override
    public int compareTo(Triple o) {
        if (this.y == o.y)
            return this.z - o.z;
        return this.y - o.y;
    }
}


import javax.print.attribute.IntegerSyntax;
import java.io.*;
import java.util.*;

/**
 * A simple template for competitive programming problems.
 */
public class Banana {
    final InputReader in = new InputReader(System.in);
    final PrintWriter out = new PrintWriter(System.out);

    final long mod = 1000000009;

    final boolean DEBUG = false;
    boolean[] taken; Point[] points;
    void solve() {
        int n = in.nextInt();
        points = new Point[n];
        for(int i=0; i<n; i++) {
            points[i] = new Point(in.nextInt(), in.nextInt(), in.nextInt(), i+1);
        }
        Arrays.sort(points);
        if (DEBUG) {
            for(int i=0; i<n; i++) {
                out.print(points[i].index + " ");
            }
            out.println();
        }
        taken = new boolean[n];
        for(int i=0; i<n; i++) {
            if(taken[i]) continue;
            int lastJ = i+1;
            while(taken[lastJ]) {
                lastJ++;
            }
            if(points[i].x==points[lastJ].x) {
                if(points[i].y==points[lastJ].y) {
                    print(i, lastJ);
                    continue;
                }
                if(points[i].z <= points[lastJ].z) {
                    print(i, lastJ);
                    continue;
                }
                for(int j=lastJ; j<n; j++) {
                    if(taken[j]) continue;
                    if(!(points[j].x==points[lastJ].x && points[j].y==points[lastJ].y && points[j].z<=points[i].z)) {
                        break;
                    }
                    lastJ = j;
                }
                print(i, lastJ);
            } else {
                if(points[i].y <= points[lastJ].y) {
                    for(int j=lastJ; j<n; j++) {
                        if(taken[j]) continue;
                        if(!(points[j].x==points[lastJ].x && points[j].y==points[lastJ].y && points[j].z<=points[i].z)) {
                            break;
                        }
                        lastJ = j;
                    }
                    print(i, lastJ);
                    continue;
                }
                for(int j=lastJ; j<n; j++) {
                    if(taken[j]) continue;
                    if(!(points[j].x==points[lastJ].x && points[j].y<=points[i].y)) {
                        break;
                    }
                    lastJ = j;
                }
                for(int j=lastJ; j>i; j--) {
                    if(taken[j]) continue;
                    if(!(points[j].x==points[lastJ].x && points[j].y==points[lastJ].y)) {
                        break;
                    }
                    lastJ = j;
                }
                for(int j=lastJ; j<n; j++) {
                    if(taken[j]) continue;
                    if(!(points[j].x==points[lastJ].x && points[j].y==points[lastJ].y && points[j].z<=points[i].z)) {
                        break;
                    }
                    lastJ = j;
                }
                print(i, lastJ);
            }
        }
    }

    private void print(int i, int j) {
        out.println(points[i].index + " " + points[j].index);
        taken[i] = true;
        taken[j] = true;
    }

    private boolean inBetween(Point p1, Point p2, Point p3) {
        int minX = Math.min(p1.x, p2.x);
        int minY = Math.min(p1.y, p2.y);
        int minZ = Math.min(p1.z, p2.z);
        int maxX = Math.max(p1.x, p2.x);
        int maxY = Math.max(p1.y, p2.y);
        int maxZ = Math.max(p1.z, p2.z);

        return p3.x <= maxX && p3.x >= minX && p3.y >= minY && p3.y <= maxY && p3.z <= maxZ && p3.z >= minZ;
    }

    class Point implements Comparable<Point> {
        int x; int y; int z;
        int index;
        Point(int x, int y, int z, int index) {
            this.x = x; this.y = y; this.z = z; this.index = index;
        }
        @Override
        public int compareTo(Point p) {
            int diff = this.x - p.x;
            if(diff!=0)
                return diff;
            diff = this.y - p.y;
            if(diff != 0)
                return diff;
            return this.z - p.z;
        }
    }
    public static void main(String[] args) throws FileNotFoundException { Banana s = new Banana(); s.solve(); s.out.close(); }

    public Banana() throws FileNotFoundException { }

    private static class InputReader {
        private final InputStream stream;
        private final byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;

        InputReader(InputStream stream) {
            this.stream = stream;
        }

        InputReader(String fileName) {
            InputStream stream = null;
            try {
                stream = new FileInputStream(fileName);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            this.stream = stream;
        }

        int[] nextArray(int n) {
            int[] arr = new int[n];
            for (int i = 0; i < n; i++)
                arr[i] = nextInt();
            return arr;
        }

        int[][] nextMatrix(int n, int m) {
            int[][] matrix = new int[n][m];
            for (int i = 0; i < n; i++)
                for (int j = 0; j < m; j++)
                    matrix[i][j] = nextInt();
            return matrix;
        }

        String nextLine() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isEndOfLine(c));
            return res.toString();
        }

        String nextString() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = read();
            } while (!isSpaceChar(c));
            return res.toString();
        }

        long nextLong() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            long res = 0;
            do {
                if (c < '0' || c > '9') throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        int nextInt() {
            int c = read();
            while (isSpaceChar(c))
                c = read();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9') throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        double nextDouble() {
            return Double.parseDouble(nextString());
        }

        private int read() {
            if (numChars == -1) throw new InputMismatchException();
            if (curChar >= numChars) {
                curChar = 0;
                try { numChars = stream.read(buf); }
                catch (IOException e) { throw new InputMismatchException(); }
                if (numChars <= 0) return -1;
            }
            return buf[curChar++];
        }

        private boolean isSpaceChar(int c) { return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1; }

        private boolean isEndOfLine(int c) { return c == '\n' || c == '\r' || c == -1; }
    }

}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.io.BufferedReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastScanner in = new FastScanner(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskC2 solver = new TaskC2();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskC2 {
        public void solve(int testNumber, FastScanner in, PrintWriter out) {
            int n = in.nextInt();
            Point[] p = new Point[n];
            for (int i = 0; i < n; i++) {
                p[i] = new Point();
                p[i].x = in.nextInt();
                p[i].y = in.nextInt();
                p[i].z = in.nextInt();
                p[i].id = i + 1;
            }

            Arrays.sort(p);
            List<Point> leftoversZ = new ArrayList<>();
            for (int iz = 0; iz < n; ) {
                int jz = iz;
                while (jz < n && p[iz].z == p[jz].z) {
                    ++jz;
                }

                List<Point> leftoversY = new ArrayList<>();
                for (int iy = iz; iy < jz; ) {
                    int jy = iy;
                    while (jy < n && p[iy].y == p[jy].y) {
                        ++jy;
                    }

                    for (int k = iy; k + 1 < jy; k += 2) {
                        out.println(p[k].id + " " + p[k + 1].id);
                    }
                    if ((jy - iy) % 2 != 0) {
                        leftoversY.add(p[jy - 1]);
                    }

                    iy = jy;
                }

                for (int i = 0; i + 1 < leftoversY.size(); i += 2) {
                    Point a = leftoversY.get(i);
                    Point b = leftoversY.get(i + 1);
                    out.println(a.id + " " + b.id);
                }

                if (leftoversY.size() % 2 != 0) {
                    leftoversZ.add(leftoversY.get(leftoversY.size() - 1));
                }

                iz = jz;
            }

            for (int i = 0; i + 1 < leftoversZ.size(); i += 2) {
                Point a = leftoversZ.get(i);
                Point b = leftoversZ.get(i + 1);
                out.println(a.id + " " + b.id);
            }

            if (leftoversZ.size() % 2 != 0) {
                throw new AssertionError();
            }
        }

        class Point implements Comparable<Point> {
            int x;
            int y;
            int z;
            int id;

            public int compareTo(Point o) {
                if (z != o.z) {
                    return z < o.z ? -1 : 1;
                }
                if (y != o.y) {
                    return y < o.y ? -1 : 1;
                }
                if (x != o.x) {
                    return x < o.x ? -1 : 1;
                }
                return 0;
            }

        }

    }

    static class FastScanner {
        private BufferedReader in;
        private StringTokenizer st;

        public FastScanner(InputStream stream) {
            in = new BufferedReader(new InputStreamReader(stream));
        }

        public String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(in.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return st.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.io.BufferedReader;
import java.util.Collections;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastScanner in = new FastScanner(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskC2 solver = new TaskC2();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskC2 {
        final int DIMENSIONS = 3;

        public void solve(int testNumber, FastScanner in, PrintWriter out) {
            int n = in.nextInt();
            List<Point> ps = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                Point p = new Point();
                p.id = i + 1;
                for (int j = 0; j < DIMENSIONS; j++) {
                    p.coord[j] = in.nextInt();
                }
                ps.add(p);
            }

            Collections.sort(ps);
            Point last = solve(out, ps, 0);
            if (last != null) {
                throw new AssertionError();
            }
        }

        private Point solve(PrintWriter out, List<Point> ps, int dim) {
            if (dim == DIMENSIONS) {
                if (ps.size() != 1) {
                    throw new AssertionError();
                }
                return ps.get(0);
            }

            List<Point> leftovers = new ArrayList<>();
            for (List<Point> g : groupBy(ps, dim)) {
                Point last = solve(out, g, dim + 1);
                if (last != null) {
                    leftovers.add(last);
                }
            }

            return solveOneDimensional(out, leftovers);
        }

        private Point solveOneDimensional(PrintWriter out, List<Point> ps) {
            for (int i = 0; i + 1 < ps.size(); i += 2) {
                out.println(ps.get(i).id + " " + ps.get(i + 1).id);
            }
            if (ps.size() % 2 == 1) {
                return ps.get(ps.size() - 1);
            }
            return null;
        }

        private List<List<Point>> groupBy(List<Point> ps, int dim) {
            List<List<Point>> groups = new ArrayList<>();
            for (int i = 0; i < ps.size(); ) {
                int j = i;
                while (j < ps.size() && ps.get(i).coord[dim] == ps.get(j).coord[dim]) {
                    ++j;
                }

                List<Point> cur = new ArrayList<>();
                for (int k = i; k < j; k++) {
                    cur.add(ps.get(k));
                }
                groups.add(cur);

                i = j;
            }
            return groups;
        }

        class Point implements Comparable<Point> {
            int[] coord = new int[DIMENSIONS];
            int id;

            public int compareTo(Point o) {
                for (int i = 0; i < coord.length; i++) {
                    if (coord[i] != o.coord[i]) {
                        return coord[i] < o.coord[i] ? -1 : 1;
                    }
                }
                return 0;
            }

        }

    }

    static class FastScanner {
        private BufferedReader in;
        private StringTokenizer st;

        public FastScanner(InputStream stream) {
            in = new BufferedReader(new InputStreamReader(stream));
        }

        public String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(in.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return st.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        FastScanner in = new FastScanner(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        TaskC2 solver = new TaskC2();
        solver.solve(1, in, out);
        out.close();
    }

    static class TaskC2 {
        public void solve(int testNumber, FastScanner in, PrintWriter out) {
            int n = in.nextInt();
            Point[] p = new Point[n];
            for (int i = 0; i < n; i++) {
                p[i] = new Point();
                p[i].x = in.nextInt();
                p[i].y = in.nextInt();
                p[i].z = in.nextInt();
                p[i].id = i + 1;
            }

            Arrays.sort(p);
            Point[] leftoversZ = new Point[n];
            int numLeftoversZ = 0;
            Point[] leftoversY = new Point[n];
            int numLeftoversY = 0;
            for (int iz = 0; iz < n; ) {
                int jz = iz;
                while (jz < n && p[iz].z == p[jz].z) {
                    ++jz;
                }

                numLeftoversY = 0;
                for (int iy = iz; iy < jz; ) {
                    int jy = iy;
                    while (jy < jz && p[iy].y == p[jy].y) {
                        ++jy;
                    }

                    for (int k = iy; k + 1 < jy; k += 2) {
                        out.println(p[k].id + " " + p[k + 1].id);
                    }
                    if ((jy - iy) % 2 != 0) {
                        leftoversY[numLeftoversY++] = p[jy - 1];
                    }

                    iy = jy;
                }

                for (int i = 0; i + 1 < numLeftoversY; i += 2) {
                    Point a = leftoversY[i];
                    Point b = leftoversY[i + 1];
                    out.println(a.id + " " + b.id);
                }

                if (numLeftoversY % 2 != 0) {
                    leftoversZ[numLeftoversZ++] = leftoversY[numLeftoversY - 1];
                }

                iz = jz;
            }

            for (int i = 0; i + 1 < numLeftoversZ; i += 2) {
                Point a = leftoversZ[i];
                Point b = leftoversZ[i + 1];
                out.println(a.id + " " + b.id);
            }

            if (numLeftoversZ % 2 != 0) {
                leftoversZ[numLeftoversZ++] = leftoversZ[numLeftoversZ - 1];
            }
        }

        class Point implements Comparable<Point> {
            int x;
            int y;
            int z;
            int id;

            public int compareTo(Point o) {
                if (z != o.z) {
                    return z < o.z ? -1 : 1;
                }
                if (y != o.y) {
                    return y < o.y ? -1 : 1;
                }
                if (x != o.x) {
                    return x < o.x ? -1 : 1;
                }
                return 0;
            }

        }

    }

    static class FastScanner {
        private BufferedReader in;
        private StringTokenizer st;

        public FastScanner(InputStream stream) {
            in = new BufferedReader(new InputStreamReader(stream));
        }

        public String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(in.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return st.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

    }
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.StringTokenizer;

public class c {
	static StringBuilder sol;
	public static void main(String[] args) {
		FS scan = new FS(System.in);
		int N = scan.nextInt();
		Point[] list = new Point[N];
		for(int i=0;i<N;i++)list[i] = new Point(scan.nextInt(),scan.nextInt(),scan.nextInt(),i+1);

		Arrays.sort(list, (a,b)->{
			if(a.z==b.z&&a.x==b.x)return a.y-b.y;
			else if(a.z==b.z)return a.x-b.x;
			else return a.z-b.z;
		});

		sol = new StringBuilder();

		ArrayList<Point> zord = new ArrayList<>();
		ArrayList<ArrayList<Point>> planes = new ArrayList<>();
		ArrayList<Point> tmp = new ArrayList<>();
		for(int i=0;i<N;i++) {
			if(i==0)tmp.add(list[i]);
			else if (list[i].z==list[i-1].z) {
				tmp.add(list[i]);
			} else {
				Point p = solve(tmp);
				if(p!=null)zord.add(p);
				tmp.clear();
				tmp.add(list[i]);
			}
		}

		Point lll = solve(tmp);
		if(lll!=null)zord.add(lll);

		Point prev = null;
		for(Point p : zord) {
			if(prev==null) {
				prev=p;
				continue;
			}else {
				sol.append(prev.idx+" "+p.idx+"\n");
				prev=null;
			}
		}

		System.out.println(sol);
	}

	private static Point solve(ArrayList<Point> list) {

		ArrayList<Point> tmp = new ArrayList<>();
//		System.out.println(list);
		Point prev = null;
		for(Point p : list) {
			if(prev==null) {
				prev=p;
				continue;
			}
			if(prev.x==p.x) {
				sol.append(prev.idx+" "+p.idx+"\n");
				prev = null;
			}else {
				tmp.add(prev);
				prev = p;
			}
		}
		if(prev!=null)tmp.add(prev);

		prev= null;
		for(Point p : tmp) {
			if(prev==null) {
				prev= p;
				continue;
			}else {
				sol.append(prev.idx+" "+p.idx+"\n");
				prev = null;
			}
		}
		return prev;
	}

	private static class Point {
		int x, y, z, idx;
		public Point(int x, int y, int z, int idx) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.idx=idx;
		}
		public String toString() {
			return "("+x+","+y+","+z+")";
		}
	}
	private static class FS {
		BufferedReader br;
		StringTokenizer st;

		public FS(InputStream in) {
			br = new BufferedReader(new InputStreamReader(in));
		}

		String next() {
			while (st == null || !st.hasMoreElements()) {
				try {
					st = new StringTokenizer(br.readLine());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return st.nextToken();
		}

		int nextInt() {
			return Integer.parseInt(next());
		}

		long nextLong() {
			return Long.parseLong(next());
		}

		double nextDouble() {
			return Double.parseDouble(next());
		}
	}
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.io.IOException;
import java.util.Comparator;
import java.util.ArrayList;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author Jeel Vaishnav
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        C2BalancedRemovalsHarder solver = new C2BalancedRemovalsHarder();
        solver.solve(1, in, out);
        out.close();
    }

    static class C2BalancedRemovalsHarder {
        public void solve(int testNumber, InputReader sc, PrintWriter out) {
            int n = sc.nextInt();

            Pair p[] = new Pair[n];
            for (int i = 0; i < n; ++i) {
                p[i] = new Pair(sc.nextInt(), sc.nextInt(), sc.nextInt(), i + 1);
            }
            Arrays.sort(p, new Comparator<Pair>() {

                public int compare(Pair o1, Pair o2) {
                    if (o1.x < o2.x)
                        return -1;
                    if (o1.x > o2.x)
                        return 1;
                    if (o1.y < o2.y)
                        return -1;
                    if (o1.y > o2.y)
                        return 1;
                    if (o1.z < o2.z)
                        return -1;
                    if (o1.z > o2.z)
                        return 1;
                    return 0;
                }
            });

            ArrayList<Integer> list = new ArrayList<>();
            Pair lastP = null;
            int sel[] = new int[n + 1];
            for (int i = 0; i < n; ++i) {
                if (lastP == null)
                    lastP = p[i];
                else {
                    if (lastP.x == p[i].x && lastP.y == p[i].y) {
                        list.add(lastP.ind);
                        list.add(p[i].ind);

                        sel[lastP.ind] = 1;
                        sel[p[i].ind] = 1;

                        lastP = null;
                    } else
                        lastP = p[i];
                }
            }

            lastP = null;
            for (int i = 0; i < n; ++i) {
                if (sel[p[i].ind] == 0) {
                    if (lastP == null)
                        lastP = p[i];
                    else {
                        if (lastP.x == p[i].x) {
                            list.add(lastP.ind);
                            list.add(p[i].ind);

                            sel[lastP.ind] = 1;
                            sel[p[i].ind] = 1;

                            lastP = null;
                        } else
                            lastP = p[i];
                    }
                }
            }

            lastP = null;
            for (int i = 0; i < n; ++i) {
                if (sel[p[i].ind] == 0) {
                    if (lastP == null)
                        lastP = p[i];
                    else {
                        list.add(lastP.ind);
                        list.add(p[i].ind);

                        sel[lastP.ind] = 1;
                        sel[p[i].ind] = 1;

                        lastP = null;
                    }
                }
            }

            for (int i = 0; i < n; i += 2)
                out.println(list.get(i) + " " + list.get(i + 1));
        }

        class Pair {
            int x;
            int y;
            int z;
            int ind;

            Pair(int a, int b, int c, int d) {
                x = a;
                y = b;
                z = c;
                ind = d;
            }

        }

    }

    static class InputReader {
        private InputStream stream;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private InputReader.SpaceCharFilter filter;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        public int read() {
            if (numChars == -1)
                throw new InputMismatchException();

            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }

                if (numChars <= 0)
                    return -1;
            }
            return buf[curChar++];
        }

        public int nextInt() {
            int c = read();

            while (isSpaceChar(c))
                c = read();

            int sgn = 1;

            if (c == '-') {
                sgn = -1;
                c = read();
            }

            int res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            }
            while (!isSpaceChar(c));

            return res * sgn;
        }

        public boolean isSpaceChar(int c) {
            if (filter != null)
                return filter.isSpaceChar(c);
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        public interface SpaceCharFilter {
            public boolean isSpaceChar(int ch);

        }

    }
}


import java.util.*;
import java.io.*;

public class c {
  static int oo = 1_000_000_000;
  public static void main(String[] args) {
    FS in = new FS(System.in);
    PrintWriter out = new PrintWriter(System.out);

    TreeSet<Integer> xComp = new TreeSet<Integer>();
    TreeSet<Integer> yComp = new TreeSet<Integer>();

    int n = in.nextInt();
    int[] x = new int[n];
    int[] y = new int[n];
    int[] z = new int[n];
    for(int i=0; i<n; i++) {
      x[i] = in.nextInt();
      y[i] = in.nextInt();
      z[i] = in.nextInt();
      xComp.add(x[i]);
      yComp.add(y[i]);
    }

    // HashMap<Integer, Integer> xComp2 = new HashMap<>();
    // Iterator iter = xComp.iterator();
    // int xInd = 0;
    // while(iter.hasNext()) {
    //   int num = (int)iter.next();
    //   xComp2.put(num, xInd);
    //   xInd++;
    // }

    // HashMap<Integer, Integer> yComp2 = new HashMap<>();
    // iter = yComp.iterator();
    // int yInd = 0;
    // while(iter.hasNext()) {
    //   int num = (int)iter.next();
    //   yComp2.put(num, yInd);
    //   yInd++;
    // }

    // PriorityQueue<Point> points = new PriorityQueue<Point>();
    // for(int i=0; i<n; i++) {
    //   x[i] = xComp2.get(x[i]);
    //   y[i] = yComp2.get(y[i]);
    //   points.add(new Point(x[i], y[i], i));
    // }

    PriorityQueue<Point> points = new PriorityQueue<>();
    for(int i=0; i<n; i++) {
      points.add(new Point(x[i], y[i], z[i], i+1));
    }

    PriorityQueue<Point> skippedX = new PriorityQueue<Point>();
    PriorityQueue<Point> skippedY = new PriorityQueue<Point>();
    // X sweep

    // Gotta go plane by plane. *sigh*
    while(!points.isEmpty()) {
      // grab a plane
      PriorityQueue<Point> curPlane = new PriorityQueue<>();
      int curX = points.peek().x;
      while(!points.isEmpty()) {
        Point a = points.poll();
        if(a.x == curX) {
          curPlane.add(a);
        } else {
          points.add(a);
          break;
        }
      }

      // Do the sweep on a plane
      // Y Sweep
      PriorityQueue<Point> skippedCP = new PriorityQueue<>();
      while(!curPlane.isEmpty()) {
        Point a = curPlane.poll();
        if(curPlane.isEmpty()) {
          skippedCP.add(a);
          break;
        }
        Point b = curPlane.poll();
        if(a.y != b.y) {
          curPlane.add(b);
          skippedCP.add(a);
        } else {
          out.println(a.id + " " + b.id);
        }
      }

      // Z leftovers
      // we assume they're all on same x plane.
      while(!skippedCP.isEmpty()) {
        Point a = skippedCP.poll();
        if(skippedCP.isEmpty()) {
          // Put the leftover into SkippedY
          skippedY.add(a);
          break;
        }
        Point b = skippedCP.poll();
        out.println(a.id + " " + b.id);
      }

    }

    // y sweep
    // PriorityQueue<Point> skippedY = new PriorityQueue<>();
    // while(!skippedX.isEmpty()) {
    //   Point a = skippedX.poll();
    //   if(skippedX.isEmpty()) {
    //     skippedY.add(a);
    //     break;
    //   }
    //   Point b = skippedX.poll();
    //   if(a.x != b.x) {
    //     skippedX.add(b);
    //     skippedY.add(a);
    //   } else {
    //     System.out.println(a.id + " " + b.id);
    //   }
    // }

    while(!skippedY.isEmpty()) {
      Point a = skippedY.poll();
      Point b = skippedY.poll();
      out.println(a.id + " " + b.id);
    }

    // HashMap<Integer, ArrayList<Point>> xCordPoints = new HashMap<>();
    // iter = xComp.iterator();
    // while(iter.hasNext()) {

    // }

    // boolean goingUp = true;

    out.flush();
  }

  static class Point implements Comparable<Point>{
    int x, y, z, id;
    public Point(int x, int y, int z, int id) {
      this.x = x;
      this.y = y;
      this.z = z;
      this.id = id;

    }

    public int compareTo(Point o) {
      if(x != o.x) return Integer.compare(x, o.x);
      if(y != o.y) return Integer.compare(y, o.y);
      return Integer.compare(z, o.z);
    }
  }

  static class FS {
    BufferedReader in;
    StringTokenizer token;
    public FS(InputStream stream) {
      in = new BufferedReader(new InputStreamReader(stream));
    }
    public String next() {
      if(token == null || !token.hasMoreElements()) {
        try {
          token = new StringTokenizer(in.readLine());
        } catch (Exception e) {}
        return next();
      }
      return token.nextToken();
    }
    public int nextInt() {
      return Integer.parseInt(next());
    }
  }
}
/*

6
3 1 0
0 3 0
2 2 0
1 0 0
1 3 0
0 1 0

3 6
5 1
2 4

Me:
6 2
4 5
3 1

8
0 1 1
1 0 1
1 1 0
1 1 1
2 2 2
3 2 2
2 3 2
2 2 3

4 5
1 6
2 7
3 8

mine
2 3
5 8
1 4
7 6

*/


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.AbstractMap;
import java.util.TreeMap;
import java.util.InputMismatchException;
import java.io.IOException;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author Sparsh Sanchorawala
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        C2BalancedRemovalsHarder solver = new C2BalancedRemovalsHarder();
        solver.solve(1, in, out);
        out.close();
    }

    static class C2BalancedRemovalsHarder {
        public void solve(int testNumber, InputReader s, PrintWriter w) {
            int n = s.nextInt();
            TreeMap<Integer, TreeMap<Integer, TreeMap<Integer, Integer>>> tm = new TreeMap<>();
            for (int i = 0; i < n; i++) {
                int x = s.nextInt(), y = s.nextInt(), z = s.nextInt();
                if (tm.get(x) == null)
                    tm.put(x, new TreeMap<>());
                if (tm.get(x).get(y) == null)
                    tm.get(x).put(y, new TreeMap<>());
                tm.get(x).get(y).put(z, i);
            }
            while (!tm.isEmpty()) {
                int x = tm.firstKey();
                int y = tm.get(x).firstKey();
                int z = tm.get(x).get(y).firstKey();
                int i = tm.get(x).get(y).get(z);
                tm.get(x).get(y).remove(z);
                int j = -1;
                if (tm.get(x).get(y).size() > 0) {
                    int z2 = tm.get(x).get(y).firstKey();
                    j = tm.get(x).get(y).get(z2);
                    tm.get(x).get(y).remove(z2);
                    if (tm.get(x).get(y).size() == 0)
                        tm.get(x).remove(y);
                    if (tm.get(x).size() == 0)
                        tm.remove(x);
                    w.println((i + 1) + " " + (j + 1));
                    continue;
                }
                tm.get(x).remove(y);
                if (tm.get(x).size() > 0) {
                    int y2 = 0;
                    if (tm.get(x).ceilingEntry(y) != null) {
                        y2 = tm.get(x).ceilingKey(y);
                    } else {
                        y2 = tm.get(x).lowerKey(y);
                    }
                    int z2 = 0;
                    if (tm.get(x).get(y2).ceilingEntry(z) != null) {
                        z2 = tm.get(x).get(y2).ceilingKey(z);
                    } else {
                        z2 = tm.get(x).get(y2).lowerKey(z);
                    }
                    j = tm.get(x).get(y2).get(z2);
                    tm.get(x).get(y2).remove(z2);
                    if (tm.get(x).get(y2).size() == 0)
                        tm.get(x).remove(y2);
                    if (tm.get(x).size() == 0)
                        tm.remove(x);
                    w.println((i + 1) + " " + (j + 1));
                    continue;
                }
                tm.remove(x);
                int x2 = 0;
                if (tm.ceilingEntry(x) != null) {
                    x2 = tm.ceilingKey(x);
                } else {
                    x2 = tm.lowerKey(x);
                }
                int y2 = 0;
                if (tm.get(x2).ceilingEntry(y) != null) {
                    y2 = tm.get(x2).ceilingKey(y);
                } else {
                    y2 = tm.get(x2).lowerKey(y);
                }
                int z2 = 0;
                if (tm.get(x2).get(y2).ceilingEntry(z) != null) {
                    z2 = tm.get(x2).get(y2).ceilingKey(z);
                } else {
                    z2 = tm.get(x2).get(y2).lowerKey(z);
                }
                j = tm.get(x2).get(y2).get(z2);
                tm.get(x2).get(y2).remove(z2);
                if (tm.get(x2).get(y2).size() == 0)
                    tm.get(x2).remove(y2);
                if (tm.get(x2).size() == 0)
                    tm.remove(x2);
                w.println((i + 1) + " " + (j + 1));
            }
        }

    }

    static class InputReader {
        private InputStream stream;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private InputReader.SpaceCharFilter filter;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        public int read() {
            if (numChars == -1) {
                throw new InputMismatchException();
            }
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0) {
                    return -1;
                }
            }
            return buf[curChar++];
        }

        public int nextInt() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9') {
                    throw new InputMismatchException();
                }
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public boolean isSpaceChar(int c) {
            if (filter != null) {
                return filter.isSpaceChar(c);
            }
            return isWhitespace(c);
        }

        public static boolean isWhitespace(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        public interface SpaceCharFilter {
            public boolean isSpaceChar(int ch);

        }

    }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.AbstractMap;
import java.util.TreeMap;
import java.util.InputMismatchException;
import java.io.IOException;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author Sparsh Sanchorawala
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        C2BalancedRemovalsHarder solver = new C2BalancedRemovalsHarder();
        solver.solve(1, in, out);
        out.close();
    }

    static class C2BalancedRemovalsHarder {
        public void solve(int testNumber, InputReader s, PrintWriter w) {
            int n = s.nextInt();
            TreeMap<Integer, TreeMap<Integer, TreeMap<Integer, Integer>>> tm = new TreeMap<>();
            for (int i = 0; i < n; i++) {
                int x = s.nextInt(), y = s.nextInt(), z = s.nextInt();
                if (tm.get(x) == null)
                    tm.put(x, new TreeMap<>());
                if (tm.get(x).get(y) == null)
                    tm.get(x).put(y, new TreeMap<>());
                tm.get(x).get(y).put(z, i);
            }
            while (!tm.isEmpty()) {
                int x = tm.firstKey();
                int y = tm.get(x).firstKey();
                int z = tm.get(x).get(y).firstKey();
                int i = tm.get(x).get(y).get(z);
                tm.get(x).get(y).remove(z);
                int j = -1;
                if (tm.get(x).get(y).size() > 0) {
                    int z2 = tm.get(x).get(y).firstKey();
                    j = tm.get(x).get(y).get(z2);
                    tm.get(x).get(y).remove(z2);
                    if (tm.get(x).get(y).size() == 0)
                        tm.get(x).remove(y);
                    if (tm.get(x).size() == 0)
                        tm.remove(x);
                    w.println((i + 1) + " " + (j + 1));
                    continue;
                }
                tm.get(x).remove(y);
                if (tm.get(x).size() > 0) {
                    int y2 = 0;
                    if (tm.get(x).lowerEntry(y) != null) {
                        y2 = tm.get(x).lowerKey(y);
                    } else {
                        y2 = tm.get(x).ceilingKey(y);
                    }
                    int z2 = 0;
                    if (tm.get(x).get(y2).lowerEntry(z) != null) {
                        z2 = tm.get(x).get(y2).lowerKey(z);
                    } else {
                        z2 = tm.get(x).get(y2).ceilingKey(z);
                    }
                    j = tm.get(x).get(y2).get(z2);
                    tm.get(x).get(y2).remove(z2);
                    if (tm.get(x).get(y2).size() == 0)
                        tm.get(x).remove(y2);
                    if (tm.get(x).size() == 0)
                        tm.remove(x);
                    w.println((i + 1) + " " + (j + 1));
                    continue;
                }
                tm.remove(x);
                int x2 = 0;
                if (tm.lowerEntry(x) != null) {
                    x2 = tm.lowerKey(x);
                } else {
                    x2 = tm.ceilingKey(x);
                }
                int y2 = 0;
                if (tm.get(x2).lowerEntry(y) != null) {
                    y2 = tm.get(x2).lowerKey(y);
                } else {
                    y2 = tm.get(x2).ceilingKey(y);
                }
                int z2 = 0;
                if (tm.get(x2).get(y2).lowerEntry(z) != null) {
                    z2 = tm.get(x2).get(y2).lowerKey(z);
                } else {
                    z2 = tm.get(x2).get(y2).ceilingKey(z);
                }
                j = tm.get(x2).get(y2).get(z2);
                tm.get(x2).get(y2).remove(z2);
                if (tm.get(x2).get(y2).size() == 0)
                    tm.get(x2).remove(y2);
                if (tm.get(x2).size() == 0)
                    tm.remove(x2);
                w.println((i + 1) + " " + (j + 1));
            }
        }

    }

    static class InputReader {
        private InputStream stream;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private InputReader.SpaceCharFilter filter;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        public int read() {
            if (numChars == -1) {
                throw new InputMismatchException();
            }
            if (curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (numChars <= 0) {
                    return -1;
                }
            }
            return buf[curChar++];
        }

        public int nextInt() {
            int c = read();
            while (isSpaceChar(c)) {
                c = read();
            }
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9') {
                    throw new InputMismatchException();
                }
                res *= 10;
                res += c - '0';
                c = read();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public boolean isSpaceChar(int c) {
            if (filter != null) {
                return filter.isSpaceChar(c);
            }
            return isWhitespace(c);
        }

        public static boolean isWhitespace(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        public interface SpaceCharFilter {
            public boolean isSpaceChar(int ch);

        }

    }
}


import java.io.*;
import java.util.*;

public class OMG {
    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);

        int n = sc.nextInt();
        Point[]a = new Point[n];
        for (int i = 0; i < n; i++) {
            a[i] = new Point(i + 1, sc.nextInt(), sc.nextInt(), sc.nextInt());
        }
        Arrays.sort(a);
        for (int i = 0; i < n; i+=2) {
            System.out.println(a[i].i + " " + a[i+1].i);
        }

    }
    static class Point implements Comparable<Point>{
        int i, x, y, z;
        Point(int d,int a, int b, int c){
            x = a;
            y = b;
            z = c;
            i = d;
        }

        @Override
        public int compareTo(Point o) {
            if(o.x!=x)
                return x - o.x;
            if(o.y != y)
                return y - o.y;
            return z - o.z;
        }
    }
}


import java.io.*;
import java.util.*;

public class TestGenerator {

    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        PrintWriter wr = new PrintWriter(System.out);

        int n = sc.nextInt();
        int[][] p = new int[n][4];
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < 3; ++j) {
                p[i][j] = sc.nextInt();
            }
            p[i][3] = i + 1;
        }
        Arrays.sort(p, (o1, o2) -> {
            for (int i = 0; i < o1.length; ++i) if (o1[i] != o2[i]) return o1[i] - o2[i];
            return 0;
        });
        boolean[] alive = new boolean[n];
        Arrays.fill(alive, true);
        for (int i = 0; i < n; ++i)
            if (alive[i]) {
                int j = i + 1;
                while (!alive[j]) ++j;
                int diff = 0;
                while (p[i][diff] == p[j][diff]) ++diff;
                int k = j;
                while (k < n && p[k][diff] == p[j][diff]) ++k;
                int bestScore = Integer.MAX_VALUE;
                int bt = -1;
                for (int t = j; t < k; ++t)
                    if (alive[t]) {
                        int score = 0;
                        for (int u = 0; u < 3; ++u) score += Math.abs(p[i][u] - p[t][u]);
                        if (score < bestScore) {
                            bestScore = score;
                            bt = t;
                        }
                    }
                alive[i] = false;
                alive[bt] = false;
                wr.println(p[i][3] + " " + p[bt][3]);
            }

        wr.close();
    }
    public static int[] RandomizeArray(int[] array){
        Random rgen = new Random();  // Random number generator

        for (int i=0; i<array.length; i++) {
            int randomPosition = rgen.nextInt(array.length);
            int temp = array[i];
            array[i] = array[randomPosition];
            array[randomPosition] = temp;
        }

        return array;
    }
}

class Scanner
{
    StringTokenizer st;
    BufferedReader br;

    public Scanner(InputStream s){	br = new BufferedReader(new InputStreamReader(s));}

    public String next() throws IOException
    {
        while (st == null || !st.hasMoreTokens())
            st = new StringTokenizer(br.readLine());
        return st.nextToken();
    }

    public int nextInt() throws IOException {return Integer.parseInt(next());}

    public long nextLong() throws IOException {return Long.parseLong(next());}

    public String nextLine() throws IOException {return br.readLine();}

    public double nextDouble() throws IOException
    {
        String x = next();
        StringBuilder sb = new StringBuilder("0");
        double res = 0, f = 1;
        boolean dec = false, neg = false;
        int start = 0;
        if(x.charAt(0) == '-')
        {
            neg = true;
            start++;
        }
        for(int i = start; i < x.length(); i++)
            if(x.charAt(i) == '.')
            {
                res = Long.parseLong(sb.toString());
                sb = new StringBuilder("0");
                dec = true;
            }
            else
            {
                sb.append(x.charAt(i));
                if(dec)
                    f *= 10;
            }
        res += Long.parseLong(sb.toString()) / f;
        return res * (neg?-1:1);
    }

    public boolean ready() throws IOException {return br.ready();}

}


import java.io.*;
import java.lang.reflect.Array;
import java.math.BigInteger;
import java.util.*;

 public class q5 {

	public static void main(String[] args) throws IOException {

		Reader.init(System.in);
		PrintWriter out=new PrintWriter(System.out);
		int n=Reader.nextInt();
		Point[] arr=new Point[n];
		for(int i=0;i<n;i++) {
			arr[i]=new Point(Reader.nextInt(),Reader.nextInt(),Reader.nextInt(),i+1);
		}
		int[] vis=new int[n];
		for(int i=0;i<n/2;i++) {
			int v1=-1,v2=-1;
			for(int j=0;j<n;j++) {
				if(vis[j]==0) {
					if(v1==-1) {
						v1=j;
					}
					else if(v2==-1) {
						v2=j;
					}
					else {
						int x1=Math.min(arr[v1].a, arr[v2].a);
						int x2=Math.max(arr[v1].a, arr[v2].a);
						int y1=Math.min(arr[v1].b, arr[v2].b);
						int y2=Math.max(arr[v1].b, arr[v2].b);
						int z1=Math.min(arr[v1].c, arr[v2].c);
						int z2=Math.max(arr[v1].c, arr[v2].c);
						if(x1<=arr[j].a && arr[j].a<=x2 && y1<=arr[j].b && arr[j].b<=y2 && z1<=arr[j].c && arr[j].c<=z2) {
							v1=j;
						}
					}
				}
			}
			vis[v1]=1;vis[v2]=1;
			out.println((v1+1)+" "+(v2+1));
		}

		out.flush();
	}
}

class Point{
	int a,b,c,ind;
	Point(int aa,int bb, int cc,int i){
		a=aa;b=bb;c=cc;ind=i;
	}
}

class Reader {
    static BufferedReader reader;
    static StringTokenizer tokenizer;
    /** call this method to initialize reader for InputStream */
    static void init() throws IOException {
    	 reader = new BufferedReader(
                 new FileReader("input.txt"));
    tokenizer = new StringTokenizer("");
    }
    static void init(InputStream input) {
        reader = new BufferedReader(
                     new InputStreamReader(input) );
        tokenizer = new StringTokenizer("");
    }
    /** get next word */
    static String nextLine() throws IOException{
    	return reader.readLine();
    }
    static String next() throws IOException {
        while ( ! tokenizer.hasMoreTokens() ) {
            //TODO add check for eof if necessary
            tokenizer = new StringTokenizer(
                   reader.readLine() );
        }
        return tokenizer.nextToken();
    }
    static int nextInt() throws IOException {
        return Integer.parseInt( next() );
    }
    static long nextLong() throws IOException {
        return Long.parseLong( next() );
    }
    static double nextDouble() throws IOException {
        return Double.parseDouble( next() );
    }
}


import java.io.*;
import java.lang.reflect.Array;
import java.math.BigInteger;
import java.util.*;

 public class q5 {

	public static void main(String[] args) throws IOException {

		Reader.init(System.in);
		PrintWriter out=new PrintWriter(System.out);
		int n=Reader.nextInt();
		TreeMap<Integer,NoD> xmap=new TreeMap<Integer,NoD>();
		TreeMap<Integer,TreeSet<Integer>> count=new TreeMap<Integer,TreeSet<Integer>>();
		TreeMap<Integer,Integer> c2=new TreeMap<Integer,Integer>();
		for(int i=0;i<n;i++) {
			int x=Reader.nextInt(),y=Reader.nextInt(),z=Reader.nextInt();
			if(!c2.containsKey(x)) c2.put(x, 0);
			int v=c2.get(x);
			if(v!=0) {
				count.get(v).remove(x);
				if(count.get(v).size()==0) count.remove(v);
			}
			v++;
			c2.put(x, v);
			if(!count.containsKey(v)) count.put(v, new TreeSet<Integer>());
			count.get(v).add(x);
			if(!xmap.containsKey(x)) {
				xmap.put(x,new NoD());
			}
			xmap.get(x).insert(y, z, i+1);

		}
		for(int i=0;i<n/2;i++) {
			int a=count.lastKey();
			if(a==1) {
				int x1=count.get(a).pollFirst();
				int x2=count.get(a).pollFirst();
				int i1=xmap.get(x1).get2(),i2=xmap.get(x2).get2();
				out.println(i1+" "+i2);
			}
			else {
				int x=count.get(a).pollFirst();
				if(count.get(a).size()==0) count.remove(a);
				N3 ans=xmap.get(x).get();
				out.println(ans.i1+" "+ans.i2);
				a-=2;
				if(a>0) {
					if(!count.containsKey(a)) count.put(a, new TreeSet<Integer>());
					count.get(a).add(x);
				}
			}
		}

		out.flush();
	}
}
 class N3{
	 int i1, i2;
	 N3(int ii1, int ii2){
		 i1=ii1;i2=ii2;
	 }
 }
class NoD{
	TreeMap<Integer,N2> tm;
	TreeMap<Integer,TreeSet<Integer>> count;
	TreeMap<Integer,Integer> c2;
	NoD(){
		tm=new TreeMap<Integer,N2>();
		count=new TreeMap<Integer,TreeSet<Integer>>();
		c2=new TreeMap<Integer,Integer>();
	}
	void insert(int y, int z, int ind) {
		if(!c2.containsKey(y)) {
			c2.put(y, 0);
		}
		int v=c2.get(y);
		if(v!=0) {
			count.get(v).remove(y);
			if(count.get(v).size()==0) count.remove(v);
		}
		v++;
		c2.put(y, v);
		if(!count.containsKey(v)) count.put(v, new TreeSet<Integer>());
		count.get(v).add(y);
		if(!tm.containsKey(y)) tm.put(y, new N2());
		tm.get(y).insert(z, ind);
	}

	N3 get() {
		int a=count.lastKey();
		if(a==1) {
			int y1=count.get(a).pollFirst();
			int y2=count.get(a).pollFirst();
			int i1=tm.get(y1).get(),i2=tm.get(y2).get();
			return new N3(i1,i2);
		}
		else {
			int y=count.get(a).pollFirst();
			if(count.get(a).size()==0) count.remove(a);
			int i1=tm.get(y).get();
			int i2=tm.get(y).get();
			a-=2;
			if(a>0) {
				if(!count.containsKey(a))count.put(a, new TreeSet<Integer>());
				count.get(a).add(y);
			}
			return new N3(i1,i2);
		}

	}
	int get2() {
		int y=count.get(1).pollFirst();
		int a=tm.get(y).get();
		tm.remove(y);
		return a;
	}
}

class N2
{
	TreeMap<Integer,Integer> tm;
	N2(){
		tm=new TreeMap<Integer,Integer>();
	}
	void insert(int z, int y) {
		tm.put(z, y);
	}

	int get() {
		int z=tm.lastKey();

		int c= tm.get(z);
		tm.remove(z);
		return c;
	}
}

class Reader {
    static BufferedReader reader;
    static StringTokenizer tokenizer;
    /** call this method to initialize reader for InputStream */
    static void init() throws IOException {
    	 reader = new BufferedReader(
                 new FileReader("input.txt"));
    tokenizer = new StringTokenizer("");
    }
    static void init(InputStream input) {
        reader = new BufferedReader(
                     new InputStreamReader(input) );
        tokenizer = new StringTokenizer("");
    }
    /** get next word */
    static String nextLine() throws IOException{
    	return reader.readLine();
    }
    static String next() throws IOException {
        while ( ! tokenizer.hasMoreTokens() ) {
            //TODO add check for eof if necessary
            tokenizer = new StringTokenizer(
                   reader.readLine() );
        }
        return tokenizer.nextToken();
    }
    static int nextInt() throws IOException {
        return Integer.parseInt( next() );
    }
    static long nextLong() throws IOException {
        return Long.parseLong( next() );
    }
    static double nextDouble() throws IOException {
        return Double.parseDouble( next() );
    }
}


import java.io.*;
import java.util.*;

public class C implements Runnable {
	public static void main (String[] args) {new Thread(null, new C(), "_cf", 1 << 28).start();}

	public void run() {
		FastScanner fs = new FastScanner();
		PrintWriter out = new PrintWriter(System.out);
		System.err.println("");

		int n = fs.nextInt();
		Point[] a = new Point[n];
		for(int i = 0; i < n; i++) a[i] = new Point(fs.nextInt(), fs.nextInt(), fs.nextInt(), i);
		Arrays.sort(a);
		boolean[] removed = new boolean[n];

		int[][] res = new int[n/2][2];
		int ptr = 0;
		for(int i = 0; i < n; i++) {
			int j = i;
			while(j < n && a[i].z == a[j].z) j++;

			for(int k = i; k < j-1; k++) {
				if(a[k].x == a[k+1].x) {
					removed[a[k].id] = removed[a[k+1].id] = true;
					res[ptr][0] = a[k].id+1; res[ptr][1] = a[k+1].id+1;
					ptr++;
					k++;
				}
			}
			for(int k = i; k < j; k++) if(!removed[a[k].id]) {
				int l = k+1;
				while(l < j && removed[a[l].id]) l++;
				if(l == j) break;

				removed[a[k].id] = removed[a[l].id] = true;
				res[ptr][0] = a[k].id+1; res[ptr][1] = a[l].id+1;
				ptr++;
				k = l;
			}

			i = --j;
		}

		for(int i = 0; i < n; i++) if(!removed[a[i].id]) {
			int j = i+1;
			while(j < n && removed[a[j].id]) j++;
			removed[a[i].id] = removed[a[j].id] = true;
			res[ptr][0] = a[i].id+1; res[ptr][1] = a[j].id+1;
			ptr++;
			i = j;
		}

		for(int i = 0; i < n/2; i++) {
			out.println(res[i][0] + " " + res[i][1]);
		}

		out.close();
	}

	class Point implements Comparable<Point> {
		int x, y, z, id;
		Point(int a, int b, int c, int i) {
			x = a; y = b; z = c;
			id = i;
		}
		public int compareTo(Point p) {
			int c = Integer.compare(z, p.z);
			if(c == 0) c = Integer.compare(x, p.x);
			if(c == 0) c = Integer.compare(y, p.y);
			return c;
		}
	}

	class FastScanner {
		public int BS = 1<<16;
		public char NC = (char)0;
		byte[] buf = new byte[BS];
		int bId = 0, size = 0;
		char c = NC;
		double num = 1;
		BufferedInputStream in;

		public FastScanner() {
			in = new BufferedInputStream(System.in, BS);
		}

		public FastScanner(String s) {
			try {
				in = new BufferedInputStream(new FileInputStream(new File(s)), BS);
			}
			catch (Exception e) {
				in = new BufferedInputStream(System.in, BS);
			}
		}

		public char nextChar(){
			while(bId==size) {
				try {
					size = in.read(buf);
				}catch(Exception e) {
					return NC;
				}
				if(size==-1)return NC;
				bId=0;
			}
			return (char)buf[bId++];
		}

		public int nextInt() {
			return (int)nextLong();
		}

		public long nextLong() {
			num=1;
			boolean neg = false;
			if(c==NC)c=nextChar();
			for(;(c<'0' || c>'9'); c = nextChar()) {
				if(c=='-')neg=true;
			}
			long res = 0;
			for(; c>='0' && c <='9'; c=nextChar()) {
				res = (res<<3)+(res<<1)+c-'0';
				num*=10;
			}
			return neg?-res:res;
		}

		public double nextDouble() {
			double cur = nextLong();
			return c!='.' ? cur:cur+nextLong()/num;
		}

		public String next() {
			StringBuilder res = new StringBuilder();
			while(c<=32)c=nextChar();
			while(c>32) {
				res.append(c);
				c=nextChar();
			}
			return res.toString();
		}

		public String nextLine() {
			StringBuilder res = new StringBuilder();
			while(c<=32)c=nextChar();
			while(c!='\n') {
				res.append(c);
				c=nextChar();
			}
			return res.toString();
		}

		public boolean hasNext() {
			if(c>32)return true;
			while(true) {
				c=nextChar();
				if(c==NC)return false;
				else if(c>32)return true;
			}
		}

		public int[] nextIntArray(int n) {
			int[] res = new int[n];
			for(int i = 0; i < n; i++) res[i] = nextInt();
			return res;
		}

	}

}


import java.io.*;
import java.util.*;

public class C implements Runnable {
	public static void main (String[] args) {new Thread(null, new C(), "_cf", 1 << 28).start();}

	public void run() {
		FastScanner fs = new FastScanner();
		PrintWriter out = new PrintWriter(System.out);
		System.err.println("");

		int n = fs.nextInt();
		Point[] a = new Point[n];
		for(int i = 0; i < n; i++) a[i] = new Point(fs.nextInt(), fs.nextInt(), fs.nextInt(), i);
		Arrays.sort(a);
		boolean[] removed = new boolean[n];

		int[][] res = new int[n/2][2];
		int ptr = 0;
		for(int i = 0; i < n; i++) {
			int j = i;
			while(j < n && a[i].z == a[j].z) j++;

			for(int k = i; k < j-1; k++) {
				if(a[k].x == a[k+1].x) {
					removed[a[k].id] = removed[a[k+1].id] = true;
					res[ptr][0] = a[k].id+1; res[ptr][1] = a[k+1].id+1;
					ptr++;
				}
			}
			for(int k = i; k < j; k++) if(!removed[a[k].id]) {
				int l = k+1;
				while(l < j && removed[a[l].id]) l++;
				if(l == j) break;

				removed[a[k].id] = removed[a[l].id] = true;
				res[ptr][0] = a[k].id+1; res[ptr][1] = a[l].id+1;
				ptr++;
				k = l;
			}

			i = --j;
		}

		for(int i = 0; i < n; i++) if(!removed[a[i].id]) {
			int j = i+1;
			while(j < n && removed[a[j].id]) j++;
			removed[a[i].id] = removed[a[j].id] = true;
			res[ptr][0] = a[i].id+1; res[ptr][1] = a[j].id+1;
			ptr++;
			i = j;
		}

		for(int i = 0; i < n/2; i++) {
			out.println(res[i][0] + " " + res[i][1]);
		}

		out.close();
	}

	class Point implements Comparable<Point> {
		int x, y, z, id;
		Point(int a, int b, int c, int i) {
			x = a; y = b; z = c;
			id = i;
		}
		public int compareTo(Point p) {
			int c = Integer.compare(z, p.z);
			if(c == 0) c = Integer.compare(x, p.x);
			if(c == 0) c = Integer.compare(y, p.y);
			return c;
		}
	}

	class FastScanner {
		public int BS = 1<<16;
		public char NC = (char)0;
		byte[] buf = new byte[BS];
		int bId = 0, size = 0;
		char c = NC;
		double num = 1;
		BufferedInputStream in;

		public FastScanner() {
			in = new BufferedInputStream(System.in, BS);
		}

		public FastScanner(String s) {
			try {
				in = new BufferedInputStream(new FileInputStream(new File(s)), BS);
			}
			catch (Exception e) {
				in = new BufferedInputStream(System.in, BS);
			}
		}

		public char nextChar(){
			while(bId==size) {
				try {
					size = in.read(buf);
				}catch(Exception e) {
					return NC;
				}
				if(size==-1)return NC;
				bId=0;
			}
			return (char)buf[bId++];
		}

		public int nextInt() {
			return (int)nextLong();
		}

		public long nextLong() {
			num=1;
			boolean neg = false;
			if(c==NC)c=nextChar();
			for(;(c<'0' || c>'9'); c = nextChar()) {
				if(c=='-')neg=true;
			}
			long res = 0;
			for(; c>='0' && c <='9'; c=nextChar()) {
				res = (res<<3)+(res<<1)+c-'0';
				num*=10;
			}
			return neg?-res:res;
		}

		public double nextDouble() {
			double cur = nextLong();
			return c!='.' ? cur:cur+nextLong()/num;
		}

		public String next() {
			StringBuilder res = new StringBuilder();
			while(c<=32)c=nextChar();
			while(c>32) {
				res.append(c);
				c=nextChar();
			}
			return res.toString();
		}

		public String nextLine() {
			StringBuilder res = new StringBuilder();
			while(c<=32)c=nextChar();
			while(c!='\n') {
				res.append(c);
				c=nextChar();
			}
			return res.toString();
		}

		public boolean hasNext() {
			if(c>32)return true;
			while(true) {
				c=nextChar();
				if(c==NC)return false;
				else if(c>32)return true;
			}
		}

		public int[] nextIntArray(int n) {
			int[] res = new int[n];
			for(int i = 0; i < n; i++) res[i] = nextInt();
			return res;
		}

	}

}


import java.io.*;
import java.util.*;

public class C implements Runnable {
	public static void main (String[] args) {new Thread(null, new C(), "_cf", 1 << 28).start();}

	public void run() {
		FastScanner fs = new FastScanner();
		PrintWriter out = new PrintWriter(System.out);
		System.err.println("");

		int n = fs.nextInt();
		Point[] a = new Point[n];
		for(int i = 0; i < n; i++) a[i] = new Point(fs.nextInt(), fs.nextInt(), fs.nextInt(), i);
		Arrays.sort(a);
		boolean[] removed = new boolean[n];

		int[][] res = new int[n/2][2];
		int ptr = 0;
		for(int i = 0; i < n; i++) {
			int j = i;
			while(j < n && a[i].z == a[j].z) j++;

			for(int k = i; k < j-1; k++) {
				if(a[k].x == a[k+1].x) {
					removed[a[k].id] = removed[a[k+1].id] = true;
					res[ptr][0] = a[k].id+1; res[ptr][1] = a[k+1].id+1;
					ptr++;
				}
			}
			for(int k = i; k < j; k++) if(!removed[a[k].id]) {
				int l = k+1;
				while(l < j && removed[a[l].id]) l++;
				if(l == j) break;

				removed[a[k].id] = removed[a[l].id] = true;
				res[ptr][0] = a[k].id+1; res[ptr][1] = a[l].id+1;
				ptr++;
				k = l;
			}

			i = --j;
		}

		for(int i = 0; i < n; i++) if(!removed[i]) {
			int j = i+1;
			while(j < n && removed[j]) j++;
			removed[i] = removed[j] = true;
			res[ptr][0] = a[i].id+1; res[ptr][1] = a[j].id+1;
			ptr++;
			i = j;
		}

		for(int i = 0; i < n/2; i++) {
			out.println(res[i][0] + " " + res[i][1]);
		}

		out.close();
	}

	class Point implements Comparable<Point> {
		int x, y, z, id;
		Point(int a, int b, int c, int i) {
			x = a; y = b; z = c;
			id = i;
		}
		public int compareTo(Point p) {
			int c = Integer.compare(z, p.z);
			if(c == 0) c = Integer.compare(x, p.x);
			if(c == 0) c = Integer.compare(y, p.y);
			return c;
		}
	}

	class FastScanner {
		public int BS = 1<<16;
		public char NC = (char)0;
		byte[] buf = new byte[BS];
		int bId = 0, size = 0;
		char c = NC;
		double num = 1;
		BufferedInputStream in;

		public FastScanner() {
			in = new BufferedInputStream(System.in, BS);
		}

		public FastScanner(String s) {
			try {
				in = new BufferedInputStream(new FileInputStream(new File(s)), BS);
			}
			catch (Exception e) {
				in = new BufferedInputStream(System.in, BS);
			}
		}

		public char nextChar(){
			while(bId==size) {
				try {
					size = in.read(buf);
				}catch(Exception e) {
					return NC;
				}
				if(size==-1)return NC;
				bId=0;
			}
			return (char)buf[bId++];
		}

		public int nextInt() {
			return (int)nextLong();
		}

		public long nextLong() {
			num=1;
			boolean neg = false;
			if(c==NC)c=nextChar();
			for(;(c<'0' || c>'9'); c = nextChar()) {
				if(c=='-')neg=true;
			}
			long res = 0;
			for(; c>='0' && c <='9'; c=nextChar()) {
				res = (res<<3)+(res<<1)+c-'0';
				num*=10;
			}
			return neg?-res:res;
		}

		public double nextDouble() {
			double cur = nextLong();
			return c!='.' ? cur:cur+nextLong()/num;
		}

		public String next() {
			StringBuilder res = new StringBuilder();
			while(c<=32)c=nextChar();
			while(c>32) {
				res.append(c);
				c=nextChar();
			}
			return res.toString();
		}

		public String nextLine() {
			StringBuilder res = new StringBuilder();
			while(c<=32)c=nextChar();
			while(c!='\n') {
				res.append(c);
				c=nextChar();
			}
			return res.toString();
		}

		public boolean hasNext() {
			if(c>32)return true;
			while(true) {
				c=nextChar();
				if(c==NC)return false;
				else if(c>32)return true;
			}
		}

		public int[] nextIntArray(int n) {
			int[] res = new int[n];
			for(int i = 0; i < n; i++) res[i] = nextInt();
			return res;
		}

	}

}


import java.io.*;
import java.util.*;

public class C implements Runnable {
	public static void main (String[] args) {new Thread(null, new C(), "_cf", 1 << 28).start();}

	public void run() {
		FastScanner fs = new FastScanner();
		PrintWriter out = new PrintWriter(System.out);
		System.err.println("");

		int n = fs.nextInt();
		Point[] a = new Point[n];
		for(int i = 0; i < n; i++) a[i] = new Point(fs.nextInt(), fs.nextInt(), fs.nextInt(), i);
		Arrays.sort(a);
		boolean[] removed = new boolean[n];

		int[][] res = new int[n/2][2];
		int ptr = 0;
		for(int i = 0; i < n; i++) {
			if(i+1 < n && a[i].z == a[i+1].z) {
				res[ptr][0] = a[i].id+1;
				res[ptr][1] = a[i+1].id+1;
				ptr++;
				removed[i] = removed[i+1] = true;

				i++;
			}
		}

		for(int i = 0; i < n; i++) if(!removed[i]) {
			int j = i+1;
			while(j < n && removed[j]) j++;
			removed[i] = removed[j] = true;
			res[ptr][0] = a[i].id+1; res[ptr][1] = a[j].id+1;
			ptr++;
			i = j;
		}

		for(int i = 0; i < n/2; i++) {
			out.println(res[i][0] + " " + res[i][1]);
		}

		out.close();
	}

	class Point implements Comparable<Point> {
		int x, y, z, id;
		Point(int a, int b, int c, int i) {
			x = a; y = b; z = c;
			id = i;
		}
		public int compareTo(Point p) {
			int c = Integer.compare(z, p.z);
			if(c == 0) c = Integer.compare(x, p.x);
			if(c == 0) c = Integer.compare(y, p.y);
			return c;
		}
	}

	class FastScanner {
		public int BS = 1<<16;
		public char NC = (char)0;
		byte[] buf = new byte[BS];
		int bId = 0, size = 0;
		char c = NC;
		double num = 1;
		BufferedInputStream in;

		public FastScanner() {
			in = new BufferedInputStream(System.in, BS);
		}

		public FastScanner(String s) {
			try {
				in = new BufferedInputStream(new FileInputStream(new File(s)), BS);
			}
			catch (Exception e) {
				in = new BufferedInputStream(System.in, BS);
			}
		}

		public char nextChar(){
			while(bId==size) {
				try {
					size = in.read(buf);
				}catch(Exception e) {
					return NC;
				}
				if(size==-1)return NC;
				bId=0;
			}
			return (char)buf[bId++];
		}

		public int nextInt() {
			return (int)nextLong();
		}

		public long nextLong() {
			num=1;
			boolean neg = false;
			if(c==NC)c=nextChar();
			for(;(c<'0' || c>'9'); c = nextChar()) {
				if(c=='-')neg=true;
			}
			long res = 0;
			for(; c>='0' && c <='9'; c=nextChar()) {
				res = (res<<3)+(res<<1)+c-'0';
				num*=10;
			}
			return neg?-res:res;
		}

		public double nextDouble() {
			double cur = nextLong();
			return c!='.' ? cur:cur+nextLong()/num;
		}

		public String next() {
			StringBuilder res = new StringBuilder();
			while(c<=32)c=nextChar();
			while(c>32) {
				res.append(c);
				c=nextChar();
			}
			return res.toString();
		}

		public String nextLine() {
			StringBuilder res = new StringBuilder();
			while(c<=32)c=nextChar();
			while(c!='\n') {
				res.append(c);
				c=nextChar();
			}
			return res.toString();
		}

		public boolean hasNext() {
			if(c>32)return true;
			while(true) {
				c=nextChar();
				if(c==NC)return false;
				else if(c>32)return true;
			}
		}

		public int[] nextIntArray(int n) {
			int[] res = new int[n];
			for(int i = 0; i < n; i++) res[i] = nextInt();
			return res;
		}

	}

}


import java.io.*;
import java.util.*;

public class Main {
	static final long MOD = 998244353;

    public static void main(String[] args) throws IOException {
        FastScanner sc = new FastScanner();
        int N = sc.nextInt();
        int[][] points = new int[N][4];
        for (int i = 0; i < N; i++) {
        	points[i][0] = i;
        	for (int j = 1; j <= 3; j++)
        		points[i][j] = sc.nextInt();
        }
        int[][] sorted = sort(points);
        TreeMap<Integer,TreeMap<Integer,TreeSet<Integer>>> tmNext = new TreeMap<Integer,TreeMap<Integer,TreeSet<Integer>>>();
        for (int[] p: points) {
        	if (tmNext.containsKey(p[1])) {
        		TreeMap<Integer,TreeSet<Integer>> tm = tmNext.get(p[1]);
        		if (tm.containsKey(p[2])) {
        			tmNext.get(p[1]).get(p[2]).add(p[3]);
        		} else {
            		TreeSet<Integer> ts = new TreeSet<Integer>();
            		ts.add(p[3]);
        			tm.put(p[2],ts);
        			tmNext.put(p[1],tm);
        		}
        	} else {
        		TreeMap<Integer,TreeSet<Integer>> tm = new TreeMap<Integer,TreeSet<Integer>>();
        		TreeSet<Integer> ts = new TreeSet<Integer>();
        		ts.add(p[3]);
        		tm.put(p[2],ts);
        		tmNext.put(p[1],tm);
        	}
        }
        boolean[] vis = new boolean[N];
        int[][] ans = new int[N/2][2];
        int index = 0;
        HashMap<Point,Integer> hm = new HashMap<Point,Integer>();
        for (int i = 0; i < N; i++) {
        	Point p = new Point(sorted[i][1],sorted[i][2],sorted[i][3]);
        	hm.put(p,sorted[i][0]);
        }
        //System.out.println(Arrays.deepToString(sorted));
        for (int i = 0; i < N; i++) {
        	if (vis[sorted[i][0]]) continue;
        	//Remove point
        	if (tmNext.get(sorted[i][1]).get(sorted[i][2]).size() == 1) {
        		if (tmNext.get(sorted[i][1]).size() == 1)
        			tmNext.remove(sorted[i][1]);
        		else
        			tmNext.get(sorted[i][1]).remove(sorted[i][2]);
        	} else {
        		tmNext.get(sorted[i][1]).get(sorted[i][2]).remove(sorted[i][3]);
        	}
    		TreeMap<Integer,TreeSet<Integer>> tm = new TreeMap<Integer,TreeSet<Integer>>();
        	Integer fx = tmNext.floorKey(sorted[i][1]);
        	Integer cx = tmNext.ceilingKey(sorted[i][1]);
        	int x = 0;
    		if (fx != null) {
        		tm = tmNext.get(fx);
        		x = fx;
        	} else {
        		tm = tmNext.get(cx);
        		x = cx;
        	}
    		TreeSet<Integer> ts = new TreeSet<Integer>();
        	Integer fy = tm.floorKey(sorted[i][2]);
        	Integer cy = tm.ceilingKey(sorted[i][2]);
        	int y = 0;
    		if (fy != null) {
        		ts = tm.get(fy);
        		y = fy;
        	} else {
        		ts = tm.get(cy);
        		y = cy;
        	}

    		int z = 0;
        	Integer fz = ts.floor(sorted[i][3]);
        	Integer cz = ts.ceiling(sorted[i][3]);
        	if (fz != null)
        		z = fz;
        	else
        		z = cz;
        	//Remove used point
        	if (tmNext.get(x).get(y).size() == 1) {
        		if (tmNext.get(x).size() == 1)
        			tmNext.remove(x);
        		else
        			tmNext.get(x).remove(y);
        	} else {
        		tmNext.get(x).get(y).remove(z);
        	}

        	Point p = new Point(x,y,z);
        	int match = hm.get(p);

        	vis[sorted[i][0]] = true;
        	vis[match] = true;
        	ans[index] = new int[]{sorted[i][0]+1,match+1};
        	index++;
        }
        StringBuilder print = new StringBuilder();
        for (int i = 0; i < N/2; i++)
        	print.append(ans[i][0] + " " + ans[i][1] + "\n");
        System.out.print(print);
    }

    static class Point {
    	public int x;
    	public int y;
    	public int z;
    	public Point(int x, int y, int z) {
    		this.x = x;
    		this.y = y;
    		this.z = z;
    	}

        @Override
        public int hashCode() {
            long hc = (961 * (long)(x)) + (31 * (long)y) + z;
            hc += 10000*MOD;
            hc %= MOD;
            return (int)hc;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Point other = (Point) obj;
            if (x != other.x || y != other.y || z != other.z)
                return false;
            return true;
        }
    }

    public static int[][] sort(int[][] array) {
    	//Sort an array (immune to quicksort TLE)
		Random rgen = new Random();
		for (int i = 0; i < array.length; i++) {
		    int randomPosition = rgen.nextInt(array.length);
		    int[] temp = array[i];
		    array[i] = array[randomPosition];
		    array[randomPosition] = temp;
		}
		Arrays.sort(array, new Comparator<int[]>() {
			  @Override
        	  public int compare(int[] arr1, int[] arr2) {
				  if (arr1[1] != arr2[1])
					  return arr1[1]-arr2[1];
				  else if (arr1[2] != arr2[2])
					  return arr1[2]-arr2[2];
				  else
					  return arr1[3]-arr2[3];
	          }
		});
		return array;
	}

    static class FastScanner {
        BufferedReader br;
        StringTokenizer st;

        public FastScanner() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException  e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }

        String nextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
}