import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;

public class Remo {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	static class Node {
		int x, y;

		Node(int x, int y) {
			this.x=x;
			this.y=y;
		}

		public String toString() {
			return this.x + " " + this.y;
		}
	}

	public static void main(String[] args) {

	    int n = sc.nextInt();
	    ArrayList<Point> al = new ArrayList();
	    for(int i = 0; i < n; i++) {
	    	al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
	    }
	    boolean[] bools = new boolean[n];
	    Collections.sort(al,new comp());
	    ArrayList<ArrayList<Point>> subs = new ArrayList();
	    subs.add(new ArrayList());
	    subs.get(subs.size()-1).add(al.get(0));
	    for(int i = 1; i < n; i++) {
	    	long last = subs.get(subs.size()-1).get(subs.get(subs.size()-1).size()-1).x;
	    	long now = al.get(i).x;
	    	if(last==now) {
	    		subs.get(subs.size()-1).add(al.get(i));
	    	} else {
	    		subs.add(new ArrayList());
	    		subs.get(subs.size()-1).add(al.get(i));
	    	}
	    }
	    int ind = 0;
//	    for(ArrayList<Point> als : subs) {
//	    	for(Point p : als) out.println(p);
//	    	out.println();
//	    }
	    ArrayList<Node> res = new ArrayList();
	    for(int i = 0; i < subs.size(); i++) {
	    	ArrayList<Point> als = subs.get(i);
	    	int lastind = -1;
	    	int firstind = ind;
	    	for(int j = 0; j < als.size(); j++) {
	    		if(j==als.size()-1) {
		    		bools[ind]=true;
		    		lastind = ind;
//		    		out.println(Arrays.toString(bools) + " " + ind);
		    		ind++;
		    		break;
		    	}
		    	if(als.get(j).y==als.get(j+1).y) {
		    		Node tmp = new Node(als.get(j).ind+1,als.get(j+1).ind+1);
		    		res.add(tmp);
//		    		out.println((als.get(j).ind+1) + " " + (als.get(j+1).ind+1));
		    		j++;
		    		ind++;
//		    		out.println(Arrays.toString(bools) + " " + ind);
		    	} else {
		    		bools[ind] = true;
		    		lastind = ind;
//		    		out.println(Arrays.toString(bools) + " " + ind);
		    	}
		    	ind++;
	    	}
//	    	if(i==1) out.println(firstind + " " + lastind);
	    	for(int j = firstind; j <= lastind; j++) {
//	    		out.println(j);
		    	if(bools[j]) {
		    		for(int k = j+1; k <= lastind; k++) {
		    			if(bools[k]) {
//		    				out.println(j + " " + k);
		    				Node tmp = new Node(al.get(j).ind+1,al.get(k).ind+1);
		    				res.add(tmp);
//		    				out.println((al.get(j).ind+1) + " " + (al.get(k).ind+1));
		    				bools[j] = false;
		    				bools[k] = false;
		    				j=k;
		    				break;
		    			}
		    		}
		    	}
		    }
	    }
	    for(int i = 0; i < n; i++) {
	    	if(bools[i]) {
	    		for(int j = i+1; j < n; j++) {
	    			if(bools[j]) {
	    				Node tmp = new Node(al.get(i).ind+1,al.get(j).ind+1);
	    				res.add(tmp);
//	    				out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
	    				i=j+1;
	    				break;
	    			}
	    		}
	    	}
	    }
//	    for(boolean b : bools) out.print(b + " ");
	    if(res.size()!=n/2) out.println(res.size());
	    else
	    	for(Node nd : res) out.println(nd);
	    out.close();
	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;

public class Remo {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	static class Node {
		int x, y;

		Node(int x, int y) {
			this.x=x;
			this.y=y;
		}

		public String toString() {
			return this.x + " " + this.y;
		}
	}

	public static void main(String[] args) {

	    int n = sc.nextInt();
	    ArrayList<Point> al = new ArrayList();
	    for(int i = 0; i < n; i++) {
	    	al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
	    }
	    boolean[] bools = new boolean[n];
	    Collections.sort(al,new comp());
	    ArrayList<ArrayList<Point>> subs = new ArrayList();
	    subs.add(new ArrayList());
	    subs.get(subs.size()-1).add(al.get(0));
	    for(int i = 1; i < n; i++) {
	    	long last = subs.get(subs.size()-1).get(subs.get(subs.size()-1).size()-1).x;
	    	long now = al.get(i).x;
	    	if(last==now) {
	    		subs.get(subs.size()-1).add(al.get(i));
	    	} else {
	    		subs.add(new ArrayList());
	    		subs.get(subs.size()-1).add(al.get(i));
	    	}
	    }
	    int ind = 0;
//	    for(ArrayList<Point> als : subs) {
//	    	for(Point p : als) out.println(p);
//	    	out.println();
//	    }
	    ArrayList<Node> res = new ArrayList();
	    for(int i = 0; i < subs.size(); i++) {
	    	ArrayList<Point> als = subs.get(i);
	    	int lastind = -1;
	    	int firstind = ind;
	    	for(int j = 0; j < als.size(); j++) {
	    		if(j==als.size()-1) {
		    		bools[ind]=true;
		    		lastind = ind;
//		    		out.println(Arrays.toString(bools) + " " + ind);
		    		ind++;
		    		break;
		    	}
		    	if(als.get(j).y==als.get(j+1).y) {
		    		Node tmp = new Node(als.get(j).ind+1,als.get(j+1).ind+1);
		    		res.add(tmp);
//		    		out.println((als.get(j).ind+1) + " " + (als.get(j+1).ind+1));
		    		j++;
		    		ind++;
//		    		out.println(Arrays.toString(bools) + " " + ind);
		    	} else {
		    		bools[ind] = true;
		    		lastind = ind;
//		    		out.println(Arrays.toString(bools) + " " + ind);
		    	}
		    	ind++;
	    	}
//	    	if(i==1) out.println(firstind + " " + lastind);
	    	for(int j = firstind; j <= lastind; j++) {
//	    		out.println(j);
		    	if(bools[j]) {
		    		for(int k = j+1; k <= lastind; k++) {
		    			if(bools[k]) {
//		    				out.println(j + " " + k);
		    				Node tmp = new Node(al.get(j).ind+1,al.get(k).ind+1);
		    				res.add(tmp);
//		    				out.println((al.get(j).ind+1) + " " + (al.get(k).ind+1));
		    				bools[j] = false;
		    				bools[k] = false;
		    				j=k;
		    				break;
		    			}
		    		}
		    	}
		    }
	    }
	    for(int i = 0; i < n; i++) {
	    	if(bools[i]) {
	    		for(int j = i+1; j < n; j++) {
	    			if(bools[j]) {
	    				Node tmp = new Node(al.get(i).ind+1,al.get(j).ind+1);
	    				res.add(tmp);
//	    				out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
	    				i=j+1;
	    				break;
	    			}
	    		}
	    	}
	    }
//	    for(boolean b : bools) out.print(b + " ");
	    for(Node nd : res) out.println(nd);
	    out.close();
	}

}


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

public class Remo {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	static class Node {
		int x, y;

		Node(int x, int y) {
			this.x=x;
			this.y=y;
		}

		public String toString() {
			return this.x + " " + this.y;
		}
	}

	public static void main(String[] args) {

	    int n = sc.nextInt();
	    ArrayList<Point> al = new ArrayList();
	    for(int i = 0; i < n; i++) {
	    	al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
	    }
	    boolean[] bools = new boolean[n];
	    Collections.sort(al,new comp());
//	    for(Point p : al) out.println(p);
//	    for(int i = 0; i < n; i++) {
//	    	if(i==al.size()-1) {
//	    		bools[i]=true;
//	    		break;
//	    	}
//	    	if(al.get(i).x==al.get(i+1).x) {
//	    		out.println((al.get(i).ind+1) + " " + (al.get(i+1).ind+1));
//	    		i++;
//	    	} else {
//	    		bools[i] = true;
//	    	}
//	    }
//	    for(int i = 0; i < n; i++) {
//	    	if(bools[i]) {
//	    		for(int j = i+1; j < n; j++) {
//	    			if(bools[j]) {
//	    				out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
//	    				i=j+1;
//	    				break;
//	    			}
//	    		}
//	    	}
//	    }
	    ArrayList<ArrayList<Point>> subs = new ArrayList();
	    subs.add(new ArrayList());
	    subs.get(subs.size()-1).add(al.get(0));
	    for(int i = 1; i < n; i++) {
	    	long last = subs.get(subs.size()-1).get(subs.get(subs.size()-1).size()-1).x;
	    	long now = al.get(i).x;
	    	if(last==now) {
	    		subs.get(subs.size()-1).add(al.get(i));
	    	} else {
	    		subs.add(new ArrayList());
	    		subs.get(subs.size()-1).add(al.get(i));
	    	}
	    }
//	    for(ArrayList<Point> als : subs) {
//	    	for(Point p : als) out.println(p);
//	    	out.println();
//	    }
	    int ind = 0;
	    ArrayList<Node> res = new ArrayList();
	    for(int i = 0; i < subs.size(); i++) {
	    	ArrayList<Point> als = subs.get(i);
	    	int lastind = -1;
	    	int firstind = ind;
//	    	out.println(ind);
	    	for(int j = 0; j < als.size(); j++) {
	    		if(j==als.size()-1) {
		    		bools[ind]=true;
		    		lastind = ind;
		    		ind++;
		    		break;
		    	}
		    	if(als.get(j).y==als.get(j+1).y) {
		    		Node tmp = new Node(als.get(j).ind+1,als.get(j+1).ind+1);
		    		res.add(tmp);
//		    		out.println((als.get(j).ind+1) + " " + (als.get(j+1).ind+1));
		    		j++;
		    		ind++;
		    	} else {
		    		bools[ind] = true;
		    		lastind = ind;
		    	}
		    	ind++;
	    	}
//	    	System.out.println(lastind);
	    	for(int j = firstind; j <= lastind; j++) {
//	    		System.out.println(j);
		    	if(bools[j]) {
		    		for(int k = j+1; k <= lastind; k++) {
		    			if(bools[k]) {
		    				Node tmp = new Node(al.get(j).ind+1,al.get(k).ind+1);
		    				res.add(tmp);
//		    				out.println((al.get(j).ind+1) + " " + (al.get(k).ind+1));
		    				bools[j] = false;
		    				bools[k] = false;
		    				j=k+1;
		    				break;
		    			}
		    		}
		    	}
//		    	System.out.println(lastind + " " + j);
		    }
//	    	for(boolean b : bools) out.print(b + " ");
//	    	out.println();
	    }
	    for(int i = 0; i < n; i++) {
	    	if(bools[i]) {
	    		for(int j = i+1; j < n; j++) {
	    			if(bools[j]) {
	    				Node tmp = new Node(al.get(i).ind+1,al.get(j).ind+1);
	    				res.add(tmp);
//	    				out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
	    				i=j+1;
	    				break;
	    			}
	    		}
	    	}
	    }
//	    for(boolean b : bools) out.print(b + " ");
	    for(Node nd : res) out.println(nd);
	    out.close();
	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;

public class Remo3D {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	static class Node {
		int x, y;

		Node(int x, int y) {
			this.x=x;
			this.y=y;
		}

		public String toString() {
			return this.x + " " + this.y;
		}
	}

	public static void main(String[] args) {

		int n = sc.nextInt();
		ArrayList<Point> al = new ArrayList();
		for(int i = 0; i < n; i++) {
			al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
		}
		Collections.sort(al,new comp());
		ArrayList<ArrayList<ArrayList<Integer>>> first = new ArrayList();
		first.add(new ArrayList());
		ArrayList<ArrayList<Integer>> second = first.get(0);
		second.add(new ArrayList());
		ArrayList<Integer> third = second.get(0);
		third.add(al.get(0).ind);
		for(int i = 1; i < n; i++) {
			if(al.get(i-1).x==al.get(i).x) {
				if(al.get(i).y==al.get(i-1).y) {
					third.add(al.get(i).ind);
				} else {
					second.add(new ArrayList());
					third = second.get(second.size()-1);
					third.add(al.get(i).ind);
				}
			} else {
				first.add(new ArrayList());
				second = first.get(first.size()-1);
				second.add(new ArrayList());
				third = second.get(second.size()-1);
				third.add(al.get(i).ind);
			}
		}
		boolean[] bools = new boolean[n];
		int ind = 0;
		for(int i = 0; i < first.size(); i++) {
			second = first.get(i);
			for(int j = 0; j < second.size(); j++) {
				third = second.get(j);
				for(int k = 0; k < third.size()-1; k = k+2) {
					out.println((third.get(k)+1) + " " + (third.get(k+1)+1));
					ind = ind+2;
				}
				if(third.size()%2!=0) {
					bools[ind] = true;
					ind++;
				}
			}
		}
		for(int i = 0; i < n; i++) {
			if(bools[i]) {
				for(int j = i+1; j < n; j++) {
					if(bools[j]) {
						out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
						i=j;
						break;
					}
				}
			}
		}
	    out.close();
	}

}


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

public class Remo {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	public static void main(String[] args) {

	    int n = sc.nextInt();
	    ArrayList<Point> al = new ArrayList();
	    for(int i = 0; i < n; i++) {
	    	al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
	    }
	    boolean[] bools = new boolean[n];
	    Collections.sort(al,new comp());
//	    for(Point p : al) out.println(p);
//	    for(int i = 0; i < n; i++) {
//	    	if(i==al.size()-1) {
//	    		bools[i]=true;
//	    		break;
//	    	}
//	    	if(al.get(i).x==al.get(i+1).x) {
//	    		out.println((al.get(i).ind+1) + " " + (al.get(i+1).ind+1));
//	    		i++;
//	    	} else {
//	    		bools[i] = true;
//	    	}
//	    }
//	    for(int i = 0; i < n; i++) {
//	    	if(bools[i]) {
//	    		for(int j = i+1; j < n; j++) {
//	    			if(bools[j]) {
//	    				out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
//	    				i=j+1;
//	    				break;
//	    			}
//	    		}
//	    	}
//	    }
	    ArrayList<ArrayList<Point>> subs = new ArrayList();
	    subs.add(new ArrayList());
	    subs.get(subs.size()-1).add(al.get(0));
	    for(int i = 1; i < n; i++) {
	    	long last = subs.get(subs.size()-1).get(subs.get(subs.size()-1).size()-1).x;
	    	long now = al.get(i).x;
	    	if(last==now) {
	    		subs.get(subs.size()-1).add(al.get(i));
	    	} else {
	    		subs.add(new ArrayList());
	    		subs.get(subs.size()-1).add(al.get(i));
	    	}
	    }
//	    for(ArrayList<Point> als : subs) {
//	    	for(Point p : als) out.println(p);
//	    	out.println();
//	    }
	    int ind = 0;
	    for(int i = 0; i < subs.size(); i++) {
	    	ArrayList<Point> als = subs.get(i);
	    	int lastind = -1;
	    	int firstind = ind;
	    	for(int j = 0; j < als.size(); j++) {
	    		if(j==als.size()-1) {
		    		bools[ind]=true;
		    		lastind = ind;
		    		ind++;
		    		break;
		    	}
		    	if(als.get(j).y==als.get(j+1).y) {
		    		out.println((als.get(j).ind+1) + " " + (als.get(j+1).ind+1));
		    		j++;
		    	} else {
		    		bools[ind] = true;
		    		lastind = ind;
		    	}
		    	ind++;
	    	}
//	    	System.out.println(lastind);
	    	for(int j = firstind; j <= lastind; j++) {
//	    		System.out.println(j);
		    	if(bools[j]) {
		    		for(int k = j+1; k <= lastind; k++) {
		    			if(bools[k]) {
		    				out.println((al.get(j).ind+1) + " " + (al.get(k).ind+1));
		    				bools[j] = false;
		    				bools[k] = false;
		    				j=k+1;
		    				break;
		    			}
		    		}
		    	}
//		    	System.out.println(lastind + " " + j);
		    }
	    }
	    for(int i = 0; i < n; i++) {
	    	if(bools[i]) {
	    		for(int j = i+1; j < n; j++) {
	    			if(bools[j]) {
	    				out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
	    				i=j+1;
	    				break;
	    			}
	    		}
	    	}
	    }
//	    for(boolean b : bools) out.print(b + " ");
	    out.close();
	}

}


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

public class Remo {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	public static void main(String[] args) {

	    int n = sc.nextInt();
	    ArrayList<Point> al = new ArrayList();
	    for(int i = 0; i < n; i++) {
	    	al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
	    }
	    boolean[] bools = new boolean[n];
	    Collections.sort(al,new comp());
//	    for(Point p : al) out.println(p);
	    for(int i = 0; i < n; i++) {
	    	if(i==al.size()-1) {
	    		bools[i]=true;
	    		break;
	    	}
	    	if(al.get(i).x==al.get(i+1).x) {
	    		out.println((al.get(i).ind+1) + " " + (al.get(i+1).ind+1));
	    		i++;
	    	} else {
	    		bools[i] = true;
	    	}
	    }
	    for(int i = 0; i < n; i++) {
	    	if(bools[i]) {
	    		for(int j = i+1; j < n; j++) {
	    			if(bools[j]) {
	    				out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
	    				i=j+1;
	    				break;
	    			}
	    		}
	    	}
	    }
	    out.close();
	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;

public class Remo {

	static FastReader sc = new FastReader();
	static OutputStream outputstream = System.out;
	static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	static class Node {
		int x, y;

		Node(int x, int y) {
			this.x=x;
			this.y=y;
		}

		public String toString() {
			return this.x + " " + this.y;
		}
	}

	public static void main(String[] args) {

		int n = sc.nextInt();
		ArrayList<Point> al = new ArrayList();
		for(int i = 0; i < n; i++) {
			al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
		}
		boolean[] bools = new boolean[n];
		Collections.sort(al,new comp());
		ArrayList<ArrayList<Point>> subs = new ArrayList();
		subs.add(new ArrayList());
		subs.get(subs.size()-1).add(al.get(0));
		for(int i = 1; i < n; i++) {
			long last = subs.get(subs.size()-1).get(subs.get(subs.size()-1).size()-1).x;
			long now = al.get(i).x;
			if(last==now) {
				subs.get(subs.size()-1).add(al.get(i));
			} else {
				subs.add(new ArrayList());
				subs.get(subs.size()-1).add(al.get(i));
			}
		}
		int ind = 0;
		ArrayList<Node> res = new ArrayList();
		for(int i = 0; i < subs.size(); i++) {
			ArrayList<Point> als = subs.get(i);
			int lastind = -1;
			int firstind = ind;
			for(int j = 0; j < als.size(); j++) {
				if(j==als.size()-1) {
					bools[ind]=true;
					lastind = ind;
					ind++;
					break;
				}
				if(als.get(j).y==als.get(j+1).y) {
					Node tmp = new Node(als.get(j).ind+1,als.get(j+1).ind+1);
					res.add(tmp);
					j++;
					ind++;
				} else {
					bools[ind] = true;
					lastind = ind;
				}
				ind++;
			}
			for(int j = firstind; j <= lastind; j++) {
				if(bools[j]) {
					for(int k = j+1; k <= lastind; k++) {
						if(bools[k]) {
							Node tmp = new Node(al.get(j).ind+1,al.get(k).ind+1);
							res.add(tmp);
							bools[j] = false;
							bools[k] = false;
							j=k;
							break;
						}
					}
				}
			}
		}
		for(int i = 0; i < n; i++) {
			if(bools[i]) {
				for(int j = i+1; j < n; j++) {
					if(bools[j]) {
						Node tmp = new Node(al.get(i).ind+1,al.get(j).ind+1);
						res.add(tmp);
						i=j;
						break;
					}
				}
			}
		}
		for(Node nd : res) out.println(nd);
		out.close();
	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;

import sample.Remo.Node;

public class Remo3D {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	static class Node {
		int x, y;

		Node(int x, int y) {
			this.x=x;
			this.y=y;
		}

		public String toString() {
			return this.x + " " + this.y;
		}
	}

	public static void main(String[] args) {

		int n = sc.nextInt();
		ArrayList<Point> al = new ArrayList();
		for(int i = 0; i < n; i++) {
			al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
		}
		Collections.sort(al,new comp());
		ArrayList<ArrayList<ArrayList<Integer>>> first = new ArrayList();
		first.add(new ArrayList());
		ArrayList<ArrayList<Integer>> second = first.get(0);
		second.add(new ArrayList());
		ArrayList<Integer> third = second.get(0);
		third.add(al.get(0).ind);
		for(int i = 1; i < n; i++) {
			if(al.get(i-1).x==al.get(i).x) {
				if(al.get(i).y==al.get(i-1).y) {
					third.add(al.get(i).ind);
				} else {
					second.add(new ArrayList());
					third = second.get(second.size()-1);
					third.add(al.get(i).ind);
				}
			} else {
				first.add(new ArrayList());
				second = first.get(first.size()-1);
				second.add(new ArrayList());
				third = second.get(second.size()-1);
				third.add(al.get(i).ind);
			}
		}
		boolean[] bools = new boolean[n];
		int ind = 0;
		for(int i = 0; i < first.size(); i++) {
			second = first.get(i);
			for(int j = 0; j < second.size(); j++) {
				third = second.get(j);
				for(int k = 0; k < third.size()-1; k = k+2) {
					out.println((third.get(k)+1) + " " + (third.get(k+1)+1));
					ind = ind+2;
				}
				if(third.size()%2!=0) {
					bools[ind] = true;
					ind++;
				}
			}
		}
		for(int i = 0; i < n; i++) {
			if(bools[i]) {
				for(int j = i+1; j < n; j++) {
					if(bools[j]) {
						out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
						i=j;
						break;
					}
				}
			}
		}
	    out.close();
	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;

public class Remo {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	static class Node {
		int x, y;

		Node(int x, int y) {
			this.x=x;
			this.y=y;
		}

		public String toString() {
			return this.x + " " + this.y;
		}
	}

	public static void main(String[] args) {

	    int n = sc.nextInt();
	    ArrayList<Point> al = new ArrayList();
	    for(int i = 0; i < n; i++) {
	    	al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
	    }
	    boolean[] bools = new boolean[n];
	    Collections.sort(al,new comp());
	    ArrayList<ArrayList<Point>> subs = new ArrayList();
	    subs.add(new ArrayList());
	    subs.get(subs.size()-1).add(al.get(0));
	    for(int i = 1; i < n; i++) {
	    	long last = subs.get(subs.size()-1).get(subs.get(subs.size()-1).size()-1).x;
	    	long now = al.get(i).x;
	    	if(last==now) {
	    		subs.get(subs.size()-1).add(al.get(i));
	    	} else {
	    		subs.add(new ArrayList());
	    		subs.get(subs.size()-1).add(al.get(i));
	    	}
	    }
	    int ind = 0;
//	    for(ArrayList<Point> als : subs) {
//	    	for(Point p : als) out.println(p);
//	    	out.println();
//	    }
	    ArrayList<Node> res = new ArrayList();
	    for(int i = 0; i < subs.size(); i++) {
	    	ArrayList<Point> als = subs.get(i);
	    	int lastind = -1;
	    	int firstind = ind;
	    	for(int j = 0; j < als.size(); j++) {
	    		if(j==als.size()-1) {
		    		bools[ind]=true;
		    		lastind = ind;
//		    		out.println(Arrays.toString(bools) + " " + ind);
		    		ind++;
		    		break;
		    	}
		    	if(als.get(j).y==als.get(j+1).y) {
		    		Node tmp = new Node(als.get(j).ind+1,als.get(j+1).ind+1);
		    		res.add(tmp);
//		    		out.println((als.get(j).ind+1) + " " + (als.get(j+1).ind+1));
		    		j++;
		    		ind++;
//		    		out.println(Arrays.toString(bools) + " " + ind);
		    	} else {
		    		bools[ind] = true;
		    		lastind = ind;
//		    		out.println(Arrays.toString(bools) + " " + ind);
		    	}
		    	ind++;
	    	}
//	    	if(i==1) out.println(firstind + " " + lastind);
	    	for(int j = firstind; j <= lastind; j++) {
//	    		out.println(j);
		    	if(bools[j]) {
		    		for(int k = j+1; k <= lastind; k++) {
		    			if(bools[k]) {
//		    				out.println(j + " " + k);
		    				Node tmp = new Node(al.get(j).ind+1,al.get(k).ind+1);
		    				res.add(tmp);
//		    				out.println((al.get(j).ind+1) + " " + (al.get(k).ind+1));
		    				bools[j] = false;
		    				bools[k] = false;
		    				j=k;
		    				break;
		    			}
		    		}
		    	}
		    }
//	    	out.println(Arrays.toString(bools) + " " + "hi");
	    }
	    for(int i = 0; i < n; i++) {
	    	if(bools[i]) {
	    		for(int j = i+1; j < n; j++) {
	    			if(bools[j]) {
	    				Node tmp = new Node(al.get(i).ind+1,al.get(j).ind+1);
	    				res.add(tmp);
//	    				out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
	    				i=j;
	    				break;
	    			}
	    		}
	    	}
	    }
//	    for(boolean b : bools) out.print(b + " ");
	    if(res.size()!=n/2) out.println(res.size());
	    else
	    	for(Node nd : res) out.println(nd);
	    out.close();
	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;

import sample.Remo.Point;
import sample.Remo.comp;

public class Remo3D {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	static class Node {
		int x, y;

		Node(int x, int y) {
			this.x=x;
			this.y=y;
		}

		public String toString() {
			return this.x + " " + this.y;
		}
	}

	public static void main(String[] args) {

		int n = sc.nextInt();
		ArrayList<Point> al = new ArrayList();
		for(int i = 0; i < n; i++) {
			al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
		}
		Collections.sort(al,new comp());
		ArrayList<ArrayList<ArrayList<Integer>>> first = new ArrayList();
		first.add(new ArrayList());
		ArrayList<ArrayList<Integer>> second = first.get(0);
		second.add(new ArrayList());
		ArrayList<Integer> third = second.get(0);
		third.add(al.get(0).ind);
		for(int i = 1; i < n; i++) {
			if(al.get(i-1).x==al.get(i).x) {
				if(al.get(i).y==al.get(i-1).y) {
					third.add(al.get(i).ind);
				} else {
					second.add(new ArrayList());
					third = second.get(second.size()-1);
					third.add(al.get(i).ind);
				}
			} else {
				first.add(new ArrayList());
				second = first.get(first.size()-1);
				second.add(new ArrayList());
				third = second.get(second.size()-1);
				third.add(al.get(i).ind);
			}
		}
		int ind = 0;
		for(int i = 0; i < first.size(); i++) {
			second = first.get(i);
			for(int j = 0; j < second.size(); j++) {
				third = second.get(j);
				for(int k = 0; k < third.size(); k++) {
					if(ind%2==0) {
						out.print(third.get(k)+1);
					} else {
						out.println(" " + (third.get(k)+1));
					}
					ind++;
				}
			}
		}
	    out.close();
	}

}


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

public class Remo {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	public static void main(String[] args) {

	    int n = sc.nextInt();
	    ArrayList<Point> al = new ArrayList();
	    for(int i = 0; i < n; i++) {
	    	al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
	    }
	    boolean[] bools = new boolean[n];
	    Collections.sort(al,new comp());
//	    for(Point p : al) out.println(p);
//	    for(int i = 0; i < n; i++) {
//	    	if(i==al.size()-1) {
//	    		bools[i]=true;
//	    		break;
//	    	}
//	    	if(al.get(i).x==al.get(i+1).x) {
//	    		out.println((al.get(i).ind+1) + " " + (al.get(i+1).ind+1));
//	    		i++;
//	    	} else {
//	    		bools[i] = true;
//	    	}
//	    }
//	    for(int i = 0; i < n; i++) {
//	    	if(bools[i]) {
//	    		for(int j = i+1; j < n; j++) {
//	    			if(bools[j]) {
//	    				out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
//	    				i=j+1;
//	    				break;
//	    			}
//	    		}
//	    	}
//	    }
	    ArrayList<ArrayList<Point>> subs = new ArrayList();
	    subs.add(new ArrayList());
	    subs.get(subs.size()-1).add(al.get(0));
	    for(int i = 1; i < n; i++) {
	    	long last = subs.get(subs.size()-1).get(subs.get(subs.size()-1).size()-1).x;
	    	long now = al.get(i).x;
	    	if(last==now) {
	    		subs.get(subs.size()-1).add(al.get(i));
	    	} else {
	    		subs.add(new ArrayList());
	    		subs.get(subs.size()-1).add(al.get(i));
	    	}
	    }
//	    for(ArrayList<Point> als : subs) {
//	    	for(Point p : als) out.println(p);
//	    	out.println();
//	    }
	    int ind = 0;
	    for(int i = 0; i < subs.size(); i++) {
	    	ArrayList<Point> als = subs.get(i);
	    	int lastind = -1;
	    	int firstind = ind;
//	    	out.println(ind);
	    	for(int j = 0; j < als.size(); j++) {
	    		if(j==als.size()-1) {
		    		bools[ind]=true;
		    		lastind = ind;
		    		ind++;
		    		break;
		    	}
		    	if(als.get(j).y==als.get(j+1).y) {
		    		out.println((als.get(j).ind+1) + " " + (als.get(j+1).ind+1));
		    		j++;
		    		ind++;
		    	} else {
		    		bools[ind] = true;
		    		lastind = ind;
		    	}
		    	ind++;
	    	}
//	    	System.out.println(lastind);
	    	for(int j = firstind; j <= lastind; j++) {
//	    		System.out.println(j);
		    	if(bools[j]) {
		    		for(int k = j+1; k <= lastind; k++) {
		    			if(bools[k]) {
		    				out.println((al.get(j).ind+1) + " " + (al.get(k).ind+1));
		    				bools[j] = false;
		    				bools[k] = false;
		    				j=k+1;
		    				break;
		    			}
		    		}
		    	}
//		    	System.out.println(lastind + " " + j);
		    }
//	    	for(boolean b : bools) out.print(b + " ");
//	    	out.println();
	    }
	    for(int i = 0; i < n; i++) {
	    	if(bools[i]) {
	    		for(int j = i+1; j < n; j++) {
	    			if(bools[j]) {
	    				out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
	    				i=j+1;
	    				break;
	    			}
	    		}
	    	}
	    }
//	    for(boolean b : bools) out.print(b + " ");
	    out.close();
	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;

public class Remo3D {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	static class Node {
		int x, y;

		Node(int x, int y) {
			this.x=x;
			this.y=y;
		}

		public String toString() {
			return this.x + " " + this.y;
		}
	}

	public static void main(String[] args) {

		int n = sc.nextInt();
		ArrayList<Point> al = new ArrayList();
		for(int i = 0; i < n; i++) {
			al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
		}
		Collections.sort(al,new comp());
//		for(Point p : al) out.println(p);
		ArrayList<ArrayList<ArrayList<Integer>>> first = new ArrayList();
		first.add(new ArrayList());
		ArrayList<ArrayList<Integer>> second = first.get(0);
		second.add(new ArrayList());
		ArrayList<Integer> third = second.get(0);
		third.add(al.get(0).ind);
		for(int i = 1; i < n; i++) {
			if(al.get(i-1).x==al.get(i).x) {
				if(al.get(i).y==al.get(i-1).y) {
					third.add(al.get(i).ind);
				} else {
					second.add(new ArrayList());
					third = second.get(second.size()-1);
					third.add(al.get(i).ind);
				}
			} else {
				first.add(new ArrayList());
				second = first.get(first.size()-1);
				second.add(new ArrayList());
				third = second.get(second.size()-1);
				third.add(al.get(i).ind);
			}
		}
//		for(ArrayList<ArrayList<Integer>> sec : first) {
//			for(ArrayList<Integer> fir : sec) out.println(fir.toString());
//			out.println();
//		}
		boolean[] bools = new boolean[n];
		int ind = 0;
		for(int i = 0; i < first.size(); i++) {
			second = first.get(i);
			int firstind = ind;
			int lastind = ind;
			for(int j = 0; j < second.size(); j++) {
				third = second.get(j);
				for(int k = 0; k < third.size()-1; k = k+2) {
//					out.println((third.get(k)+1) + " " + (third.get(k+1)+1));
					out.println((al.get(ind).ind+1) + " " + (al.get(ind+1).ind+1));
					ind = ind+2;
				}
				if(third.size()%2!=0) {
					bools[ind] = true;
					lastind = ind;
					ind++;
				}
			}
			for(int j = firstind; j <= lastind; j++) {
				if(bools[j]) {
					for(int k = j+1; k <= lastind; k++) {
						if(bools[k]) {
							out.println((al.get(j).ind+1) + " " + (al.get(k).ind+1));
							bools[j] = false;
							bools[k] = false;
							j = k;
							break;
						}
					}
				}
			}
		}
//		out.println(Arrays.toString(bools));
		for(int i = 0; i < n; i++) {
			if(bools[i]) {
				for(int j = i+1; j < n; j++) {
					if(bools[j]) {
						out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
						i=j;
						break;
					}
				}
			}
		}
	    out.close();
	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;

public class Remo3D {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	static class Node {
		int x, y;

		Node(int x, int y) {
			this.x=x;
			this.y=y;
		}

		public String toString() {
			return this.x + " " + this.y;
		}
	}

	public static void main(String[] args) {

		int n = sc.nextInt();
		ArrayList<Point> al = new ArrayList();
		for(int i = 0; i < n; i++) {
			al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
		}
		Collections.sort(al,new comp());
//		for(Point p : al) out.println(p);
		ArrayList<ArrayList<ArrayList<Integer>>> first = new ArrayList();
		first.add(new ArrayList());
		ArrayList<ArrayList<Integer>> second = first.get(0);
		second.add(new ArrayList());
		ArrayList<Integer> third = second.get(0);
		third.add(al.get(0).ind);
		for(int i = 1; i < n; i++) {
			if(al.get(i-1).x==al.get(i).x) {
				if(al.get(i).y==al.get(i-1).y) {
					third.add(al.get(i).ind);
				} else {
					second.add(new ArrayList());
					third = second.get(second.size()-1);
					third.add(al.get(i).ind);
				}
			} else {
				first.add(new ArrayList());
				second = first.get(first.size()-1);
				second.add(new ArrayList());
				third = second.get(second.size()-1);
				third.add(al.get(i).ind);
			}
		}
//		for(ArrayList<ArrayList<Integer>> sec : first) {
//			for(ArrayList<Integer> fir : sec) out.println(fir.toString());
//			out.println();
//		}
		boolean[] bools = new boolean[n];
		int ind = 0;
		for(int i = 0; i < first.size(); i++) {
			second = first.get(i);
			for(int j = 0; j < second.size(); j++) {
				third = second.get(j);
				for(int k = 0; k < third.size()-1; k = k+2) {
//					out.println((third.get(k)+1) + " " + (third.get(k+1)+1));
					out.println((al.get(ind).ind+1) + " " + (al.get(ind+1).ind+1));
					ind = ind+2;
				}
				if(third.size()%2!=0) {
					bools[ind] = true;
					ind++;
				}
			}
		}
//		out.println(Arrays.toString(bools));
		for(int i = 0; i < n; i++) {
			if(bools[i]) {
				for(int j = i+1; j < n; j++) {
					if(bools[j]) {
						out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
						i=j;
						break;
					}
				}
			}
		}
	    out.close();
	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;

public class Remo3D {

	static FastReader sc = new FastReader();
    static OutputStream outputstream = System.out;
    static PrintWriter out = new PrintWriter(outputstream);

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader()
		{
			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;
		}
	}

	static class Point {
		long x,y,z;
		int ind;

		Point(long x, long y, long z, int ind) {
			this.x=x;
			this.y=y;
			this.z=z;
			this.ind=ind;
		}
		public String toString() {
			return this.x + " " +  this.y + " " + this.z + " " + (this.ind+1);
		}
	}

	static class comp implements Comparator<Point> {
		public int compare(Point p1, Point p2) {
			if(p1.x==p2.x) {
				if(p1.y==p2.y) {
					if(p1.z>p2.z) return 1;
					return -1;
				} else if(p1.y>p2.y) {
					return 1;
				}
				return -1;
			} else if(p1.x>p2.x) {
				return 1;
			}
			return -1;
		}
	}

	static class Node {
		int x, y;

		Node(int x, int y) {
			this.x=x;
			this.y=y;
		}

		public String toString() {
			return this.x + " " + this.y;
		}
	}

	public static void main(String[] args) {

		int n = sc.nextInt();
		ArrayList<Point> al = new ArrayList();
		for(int i = 0; i < n; i++) {
			al.add(new Point(sc.nextLong(),sc.nextLong(),sc.nextLong(),i));
		}
		Collections.sort(al,new comp());
		ArrayList<ArrayList<ArrayList<Integer>>> first = new ArrayList();
		first.add(new ArrayList());
		ArrayList<ArrayList<Integer>> second = first.get(0);
		second.add(new ArrayList());
		ArrayList<Integer> third = second.get(0);
		third.add(al.get(0).ind);
		for(int i = 1; i < n; i++) {
			if(al.get(i-1).x==al.get(i).x) {
				if(al.get(i).y==al.get(i-1).y) {
					third.add(al.get(i).ind);
				} else {
					second.add(new ArrayList());
					third = second.get(second.size()-1);
					third.add(al.get(i).ind);
				}
			} else {
				first.add(new ArrayList());
				second = first.get(first.size()-1);
				second.add(new ArrayList());
				third = second.get(second.size()-1);
				third.add(al.get(i).ind);
			}
		}
		boolean[] bools = new boolean[n];
		int ind = 0;
		for(int i = 0; i < first.size(); i++) {
			second = first.get(i);
			int firstind = ind;
			int lastind = ind;
			for(int j = 0; j < second.size(); j++) {
				third = second.get(j);
				for(int k = 0; k < third.size()-1; k = k+2) {
					out.println((al.get(ind).ind+1) + " " + (al.get(ind+1).ind+1));
					ind = ind+2;
				}
				if(third.size()%2!=0) {
					bools[ind] = true;
					lastind = ind;
					ind++;
				}
			}
			for(int j = firstind; j <= lastind; j++) {
				if(bools[j]) {
					for(int k = j+1; k <= lastind; k++) {
						if(bools[k]) {
							out.println((al.get(j).ind+1) + " " + (al.get(k).ind+1));
							bools[j] = false;
							bools[k] = false;
							j = k;
							break;
						}
					}
				}
			}
		}
		for(int i = 0; i < n; i++) {
			if(bools[i]) {
				for(int j = i+1; j < n; j++) {
					if(bools[j]) {
						out.println((al.get(i).ind+1) + " " + (al.get(j).ind+1));
						i=j;
						break;
					}
				}
			}
		}
	    out.close();
	}

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;

public class C {
	static class Point implements Comparable<Point> {
		int x, y, z, index;
		public Point(int index, String coordinate) {
			this.index = index;
			String[] temp = coordinate.split(" ");
			this.x = Integer.parseInt(temp[0]);
			this.y = Integer.parseInt(temp[1]);
			this.z = Integer.parseInt(temp[2]);
		}

		@Override
		public int compareTo(Point o) {
			if (this.x > o.x) {
				return 1;
			} else if (this.x < o.x) {
				return -1;
			} else {
				if (this.y > o.y) {
					return 1;
				} else if (this.y < o.y) {
					return -1;
				} else {
					if (this.z > o.z) {
						return 1;
					} else if (this.z < o.z) {
						return -1;
					} else {
						return 0;
					}
				}
			}
		}

		@Override
		public String toString() {
			return "(" + this.x + ", " + this.y + ", " + this.z + ")";
		}
	}

	@SuppressWarnings("serial")
	static class Node<E> extends LinkedList<E> implements Comparable<Node<E>> {
		int label;

		Node(int label) {
			super();
			this.label = label;
		}

		@Override
		public int compareTo(Node<E> o) {
			if (this.label > o.label) {
				return 1;
			} else if (this.label < o.label) {
				return -1;
			} else {
				return 0;
			}
		}

	}

	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(input.readLine());
		Point[] a = new Point[n];
		for (int i = 0; i < n; i++) {
			a[i] = new Point(i, input.readLine());
		}
		input.close();
		Arrays.parallelSort(a);
		LinkedList<Node<Node<Point>>> searchTree= new LinkedList<>();
		searchTree.add(new Node<>(a[0].x));
		searchTree.get(0).add(new Node<>(a[0].y));
		searchTree.get(0).get(0).add(a[0]);
		int currentX = 0, currentY = 0;
		for (int i = 1; i < n; i++) {
			if (a[i].x == searchTree.get(currentX).label) {
				if (a[i].y == searchTree.get(currentX).get(currentY).label) {
					searchTree.get(currentX).get(currentY).add(a[i]);
				} else {
					searchTree.get(currentX).add(new Node<>(a[i].y));
					currentY++;
					searchTree.get(currentX).get(currentY).add(a[i]);
				}
			} else {
				searchTree.add(new Node<>(a[i].x));
				searchTree.getLast().add(new Node<>(a[i].y));
				searchTree.getLast().get(0).add(a[i]);
				currentX++;
				currentY = 0;
			}
		}
		Point[] result = new Point[n];
		for (int i = 0; i < n / 2; i++) {
			Point current = searchTree.get(0).get(0).get(0);
			result[2 * i] = current;
			if (searchTree.get(0).get(0).size() > 1) {
				result[2 * i + 1] = searchTree.get(0).get(0).get(1);
				if (searchTree.get(0).get(0).size() == 2) {
					searchTree.get(0).remove(0);
					if (searchTree.get(0).isEmpty()) {
						searchTree.remove(0);
					}
				} else {
					searchTree.get(0).get(0).remove(0);
					searchTree.get(0).get(0).remove(0);
				}
			} else if (searchTree.get(0).size() > 1){
				searchTree.get(0).remove(0);
				if (searchTree.get(0).get(0).size() == 1) {
					result[2 * i + 1] = searchTree.get(0).get(0).get(0);
					searchTree.get(0).remove(0);
					if (searchTree.get(0).isEmpty()) {
						searchTree.remove(0);
					}
				} else {
					Point min = searchTree.get(0).get(0).get(0);
					for (Point p : searchTree.get(0).get(0)) {
						if (Math.abs(min.z - current.z) > Math.abs(p.z - current.z)) {
							min = p;
						}
					}
					result[2 * i + 1] = min;
					searchTree.get(0).get(0).remove(min);
				}
			} else {
				searchTree.remove(0);
				Node<Point> minY = searchTree.get(0).get(0);
				for (Node<Point> p : searchTree.get(0)) {
					if (Math.abs(p.label - current.y) < Math.abs(minY.label - current.y)) {
						minY = p;
					}
				}
				if (minY.size() == 1) {
					result[2 * i + 1] = minY.get(0);
					searchTree.get(0).remove(minY);
					if (searchTree.get(0).isEmpty()) {
						searchTree.remove(0);
					}
				} else {
					Point min = minY.get(0);
					for (Point p : minY) {
						if (Math.abs(min.z - current.z) > Math.abs(p.z - current.z)) {
							min = p;
						}
					}
					result[2 * i + 1] = min;
					minY.remove(min);
				}
			}
		}
		StringBuilder output = new StringBuilder();
		for (int i = 0; i < n / 2; i++) {
			output.append(result[2 * i].index + 1).append(" ").append(result[2 * i + 1].index + 1).append("\n");
		}
		System.out.println(output.toString());
	}

}


import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.*;
import java.io.*;
import java.math.*;
public class Main {
    InputStream is;
    PrintWriter out;
    String INPUT = "";
    //class  Declaration

    static class pair implements Comparable < pair > {
        long x;
        long y;

        pair(long i, long j) {
            x = i;
            y = j;

        }
        public int compareTo(pair p) {
            if (this.x != p.x) {
                return Long.compare(this.x,p.x);
            } else {
                return Long.compare(this.y,p.y);
            }
        }
        public int hashCode() {
            return (x + " " + y).hashCode();
        }
        public String toString() {
            return x + " " + y;
        }
        public boolean equals(Object o) {
            pair x = (pair) o;
            return (x.x == this.x && x.y == this.y);
        }
    }
    // int[] dx = {0,0,1,-1};
    // int[] dy = {1,-1,0,0};
    // int[] ddx = {0,0,1,-1,1,-1,1,-1};
    // int[] ddy = {1,-1,0,0,1,-1,-1,1};

int inf = (int) 1e9 + 9;
long biginf  = (long)1e17 + 7 ;
long mod =  (long)1e9 + 7;

void solve() throws Exception {
int n=ni();
Long[][] points = new Long[n][3];
TreeMap<Long,TreeMap<Long,TreeMap<Long,Integer>>> coord =new TreeMap<>();
boolean used[] = new boolean[n];
for(int i=0;i<n;++i){
    points[i][0] = nl();points[i][1] = nl();points[i][2] = nl();
    //final long[] fpoints = {points[i][0],points[i][1],points[i][1]};
    final int ind = i ;

    coord.compute(points[ind][2],(key,val)->{
        if(val==null){
            TreeMap<Long,TreeMap<Long,Integer>> ycor = new TreeMap<>();
            TreeMap<Long,Integer> xcor = new TreeMap<>();
            xcor.put(points[ind][0],ind);
            ycor.put(points[ind][1],xcor);
            return ycor;
        }
        else{

            TreeMap<Long,TreeMap<Long,Integer>> ycor= val ;
            ycor.compute(points[ind][1],(kk,xcor)->{
                if(xcor==null){
                    TreeMap<Long,Integer> xx = new TreeMap<>();
                    xx.put(points[ind][0],ind);
                    return xx;
                }
                else{
                    TreeMap<Long,Integer> xx = xcor;
                    xx.put(points[ind][0],ind);
                    return xx ;
                }
            });
            return ycor;
        }
    });
}
//pn(coord);

int[] ans = new int[n];
int ians = 0;
for(long z : coord.keySet()){
    TreeMap<Long,TreeMap<Long,Integer>> ycor = coord.get(z);
    for(long y: ycor.keySet()){
        TreeMap<Long,Integer> xcor = ycor.get(y);
        for(long x : xcor.keySet()){
            ans[ians++] = xcor.get(x);
        }
    }
}
for(int i=0;i<n;i+=2){
    pn((ans[i]+1)+" "+(ans[i+1]+1));
}

//pn(Arrays.deepToString(points));

}
long dist(Long[] a, Long[]b){
    long dis = 0;
    for(int i=0;i<3;++i){
        dis+= (a[i]-b[i])*(a[i]-b[i]);
    }
    return dis;
}

long pow(long a, long b) {
        long result = 1;
        while (b > 0) {
            if (b % 2 == 1) result = (result * a) % mod;
            b /= 2;
            a = (a * a) % mod;
        }
        return result;
    }

    void print(Object o) {
        System.out.println(o);
        System.out.flush();
    }

    long gcd(long a, long b) {
        if (b == 0)
            return a;
        return gcd(b, a % b);
    }
    void run() throws Exception {
        is = INPUT.isEmpty() ? System.in : new ByteArrayInputStream(INPUT.getBytes());
        out = new PrintWriter(System.out);

        long s = System.currentTimeMillis();
        solve();
        out.flush();
        if (!INPUT.isEmpty()) tr(System.currentTimeMillis() - s + "ms");
    }

    public static void main(String[] args) throws Exception {
        new Main().run();
    }

    //output methods
    private void pn(Object o) {
        out.println(o);
    }
    private void p(Object o) {
        out.print(o);
    }
    private ArrayList < ArrayList < Integer >> ng(int n, int e) {
        ArrayList < ArrayList < Integer >> g = new ArrayList < > ();
        for (int i = 0; i <= n; ++i) g.add(new ArrayList < > ());

        for (int i = 0; i < e; ++i) {
            int u = ni(), v = ni();
            g.get(u).add(v);
            g.get(v).add(u);
        }

        return g;
    }

    private ArrayList < ArrayList < pair >> nwg(int n, int e) {
        ArrayList < ArrayList < pair >> g = new ArrayList < > ();
        for (int i = 0; i <= n; ++i) g.add(new ArrayList < > ());

        for (int i = 0; i < e; ++i) {
            int u = ni(), v = ni(), w = ni();
            g.get(u).add(new pair(w, v));
            g.get(v).add(new pair(w, u));
        }

        return g;
    }

    //input methods
    private byte[] inbuf = new byte[1024];
    public int lenbuf = 0, ptrbuf = 0;

    private int readByte() {
        if (lenbuf == -1) throw new InputMismatchException();
        if (ptrbuf >= lenbuf) {
            ptrbuf = 0;
            try {
                lenbuf = is.read(inbuf);
            } catch (IOException e) {
                throw new InputMismatchException();
            }
            if (lenbuf <= 0) return -1;
        }
        return inbuf[ptrbuf++];
    }

    private boolean isSpaceChar(int c) {
        return !(c >= 33 && c <= 126);
    }
    private int skip() {
        int b;
        while ((b = readByte()) != -1 && isSpaceChar(b));
        return b;
    }

    private double nd() {
        return Double.parseDouble(ns());
    }
    private char nc() {
        return (char) skip();
    }

    private String ns() {
        int b = skip();
        StringBuilder sb = new StringBuilder();
        while (!(isSpaceChar(b))) { // when nextLine, (isSpaceChar(b) && b != ' ')
            sb.appendCodePoint(b);
            b = readByte();
        }
        return sb.toString();
    }

    private char[] ns(int n) {
        char[] buf = new char[n];
        int b = skip(), p = 0;
        while (p < n && !(isSpaceChar(b))) {
            buf[p++] = (char) b;
            b = readByte();
        }
        return n == p ? buf : Arrays.copyOf(buf, p);
    }

    private char[][] nm(int n, int m) {
        char[][] map = new char[n][];
        for (int i = 0; i < n; i++) map[i] = ns(m);
        return map;
    }

    private int[] na(int n) {
        int[] a = new int[n];
        for (int i = 0; i < n; i++) a[i] = ni();
        return a;
    }

    private int ni() {
        int num = 0, b;
        boolean minus = false;
        while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
        if (b == '-') {
            minus = true;
            b = readByte();
        }

        while (true) {
            if (b >= '0' && b <= '9') {
                num = num * 10 + (b - '0');
            } else {
                return minus ? -num : num;
            }
            b = readByte();
        }
    }

    private long nl() {
        long num = 0;
        int b;
        boolean minus = false;
        while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
        if (b == '-') {
            minus = true;
            b = readByte();
        }

        while (true) {
            if (b >= '0' && b <= '9') {
                num = num * 10 + (b - '0');
            } else {
                return minus ? -num : num;
            }
            b = readByte();
        }
    }

    private void tr(Object...o) {
        if (INPUT.length() > 0) System.out.println(Arrays.deepToString(o));
    }
    void watch(Object...a) throws Exception {
        int i = 1;
        print("watch starts :");
        for (Object o: a) {
            //print(o);
            boolean notfound = true;
            if (o.getClass().isArray()) {

                String type = o.getClass().getName().toString();
                //print("type is "+type);
                switch (type) {
                    case "[I":
                        {
                            int[] test = (int[]) o;
                            print(i + " " + Arrays.toString(test));
                            break;
                        }
                    case "[[I":
                        {
                            int[][] obj = (int[][]) o;

                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[J":
                        {

                            long[] obj = (long[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[J":
                        {

                            long[][] obj = (long[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[D":
                        {

                            double[] obj = (double[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[D":
                        {

                            double[][] obj = (double[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[Ljava.lang.String":
                        {

                            String[] obj = (String[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[Ljava.lang.String":
                        {

                            String[][] obj = (String[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[C":
                        {
                            char[] obj = (char[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[C":
                        {

                            char[][] obj = (char[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }

                    default:
                        {
                            print(i + " type not identified");
                            break;
                        }
                }
                notfound = false;

            }
            if (o.getClass() == ArrayList.class) {
                print(i + " al: " + o);
                notfound = false;
            }
            if (o.getClass() == HashSet.class) {
                print(i + " hs: " + o);
                notfound = false;
            }
            if (o.getClass() == TreeSet.class) {
                print(i + " ts: " + o);
                notfound = false;
            }
            if (o.getClass() == TreeMap.class) {
                print(i + " tm: " + o);
                notfound = false;
            }
            if (o.getClass() == HashMap.class) {
                print(i + " hm: " + o);
                notfound = false;
            }
            if (o.getClass() == LinkedList.class) {
                print(i + " ll: " + o);
                notfound = false;
            }
            if (o.getClass() == PriorityQueue.class) {
                print(i + " pq : " + o);
                notfound = false;
            }
            if (o.getClass() == pair.class) {
                print(i + " pq : " + o);
                notfound = false;
            }

            if (notfound) {
                print(i + " unknown: " + o);
            }
            i++;
        }
        print("watch ends ");
    }

}


import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.*;
import java.io.*;
import java.math.*;
public class Main {
    InputStream is;
    PrintWriter out;
    String INPUT = "";
    //class  Declaration

    static class pair implements Comparable < pair > {
        long x;
        long y;

        pair(long i, long j) {
            x = i;
            y = j;

        }
        public int compareTo(pair p) {
            if (this.x != p.x) {
                return Long.compare(this.x,p.x);
            } else {
                return Long.compare(this.y,p.y);
            }
        }
        public int hashCode() {
            return (x + " " + y).hashCode();
        }
        public String toString() {
            return x + " " + y;
        }
        public boolean equals(Object o) {
            pair x = (pair) o;
            return (x.x == this.x && x.y == this.y);
        }
    }
    // int[] dx = {0,0,1,-1};
    // int[] dy = {1,-1,0,0};
    // int[] ddx = {0,0,1,-1,1,-1,1,-1};
    // int[] ddy = {1,-1,0,0,1,-1,-1,1};

int inf = (int) 1e9 + 9;
long biginf  = (long)1e17 + 7 ;
long mod =  (long)1e9 + 7;

void solve() throws Exception {
int n=ni();
Long[][] points = new Long[n][3];
TreeMap<Long,TreeMap<Long,TreeMap<Long,Integer>>> coord =new TreeMap<>();
boolean used[] = new boolean[n];
for(int i=0;i<n;++i){
    points[i][0] = nl();points[i][1] = nl();points[i][2] = nl();
    //final long[] fpoints = {points[i][0],points[i][1],points[i][1]};
    final int ind = i ;

    coord.compute(points[ind][2],(key,val)->{
        if(val==null){
            TreeMap<Long,TreeMap<Long,Integer>> ycor = new TreeMap<>();
            TreeMap<Long,Integer> xcor = new TreeMap<>();
            xcor.put(points[ind][0],ind+1);
            ycor.put(points[ind][1],xcor);
            return ycor;
        }
        else{

            TreeMap<Long,TreeMap<Long,Integer>> ycor= val ;
            ycor.compute(points[ind][1],(kk,xcor)->{
                if(xcor==null){
                    TreeMap<Long,Integer> xx = new TreeMap<>();
                    xx.put(points[ind][0],ind+1);
                    return xx;
                }
                else{
                    TreeMap<Long,Integer> xx = xcor;
                    xx.put(points[ind][0],ind+1);
                    return xx ;
                }
            });
            return ycor;
        }
    });
}
//pn(coord);

int[] ans = new int[n];
int ians = 0;
ArrayList<Integer> zans= new ArrayList<>();
for(long z : coord.keySet()){
    TreeMap<Long,TreeMap<Long,Integer>> ycor = coord.get(z);
    ArrayList<Integer> yans= new ArrayList<>();
    for(long y: ycor.keySet()){
        TreeMap<Long,Integer> xcor = ycor.get(y);
        ArrayList<Integer> xans = new ArrayList<>();
        for(long x : xcor.keySet()){
            //ans[ians++] = xcor.get(x);
            xans.add(xcor.get(x));
        }
        for(int i=0;i<=xans.size()-2;i+=2){
            pn(xans.get(i)+" "+xans.get(i+1));
        }
        if(xans.size()%2==1){
            yans.add(xans.get(xans.size()-1));
        }

    }
    for(int i=0;i<=yans.size()-2;i+=2){
        pn(yans.get(i)+" "+yans.get(i+1));
    }
    if(yans.size()%2==1){
        zans.add(yans.get(yans.size()-1));
    }

}
for(int i=0;i<=zans.size()-2;i+=2){
    pn(zans.get(i)+" "+zans.get(i+1));
}

//pn(coord);

//pn(Arrays.deepToString(points));

}
long dist(Long[] a, Long[]b){
    long dis = 0;
    for(int i=0;i<3;++i){
        dis+= (a[i]-b[i])*(a[i]-b[i]);
    }
    return dis;
}

long pow(long a, long b) {
        long result = 1;
        while (b > 0) {
            if (b % 2 == 1) result = (result * a) % mod;
            b /= 2;
            a = (a * a) % mod;
        }
        return result;
    }

    void print(Object o) {
        System.out.println(o);
        System.out.flush();
    }

    long gcd(long a, long b) {
        if (b == 0)
            return a;
        return gcd(b, a % b);
    }
    void run() throws Exception {
        is = INPUT.isEmpty() ? System.in : new ByteArrayInputStream(INPUT.getBytes());
        out = new PrintWriter(System.out);

        long s = System.currentTimeMillis();
        solve();
        out.flush();
        if (!INPUT.isEmpty()) tr(System.currentTimeMillis() - s + "ms");
    }

    public static void main(String[] args) throws Exception {
        new Main().run();
    }

    //output methods
    private void pn(Object o) {
        out.println(o);
    }
    private void p(Object o) {
        out.print(o);
    }
    private ArrayList < ArrayList < Integer >> ng(int n, int e) {
        ArrayList < ArrayList < Integer >> g = new ArrayList < > ();
        for (int i = 0; i <= n; ++i) g.add(new ArrayList < > ());

        for (int i = 0; i < e; ++i) {
            int u = ni(), v = ni();
            g.get(u).add(v);
            g.get(v).add(u);
        }

        return g;
    }

    private ArrayList < ArrayList < pair >> nwg(int n, int e) {
        ArrayList < ArrayList < pair >> g = new ArrayList < > ();
        for (int i = 0; i <= n; ++i) g.add(new ArrayList < > ());

        for (int i = 0; i < e; ++i) {
            int u = ni(), v = ni(), w = ni();
            g.get(u).add(new pair(w, v));
            g.get(v).add(new pair(w, u));
        }

        return g;
    }

    //input methods
    private byte[] inbuf = new byte[1024];
    public int lenbuf = 0, ptrbuf = 0;

    private int readByte() {
        if (lenbuf == -1) throw new InputMismatchException();
        if (ptrbuf >= lenbuf) {
            ptrbuf = 0;
            try {
                lenbuf = is.read(inbuf);
            } catch (IOException e) {
                throw new InputMismatchException();
            }
            if (lenbuf <= 0) return -1;
        }
        return inbuf[ptrbuf++];
    }

    private boolean isSpaceChar(int c) {
        return !(c >= 33 && c <= 126);
    }
    private int skip() {
        int b;
        while ((b = readByte()) != -1 && isSpaceChar(b));
        return b;
    }

    private double nd() {
        return Double.parseDouble(ns());
    }
    private char nc() {
        return (char) skip();
    }

    private String ns() {
        int b = skip();
        StringBuilder sb = new StringBuilder();
        while (!(isSpaceChar(b))) { // when nextLine, (isSpaceChar(b) && b != ' ')
            sb.appendCodePoint(b);
            b = readByte();
        }
        return sb.toString();
    }

    private char[] ns(int n) {
        char[] buf = new char[n];
        int b = skip(), p = 0;
        while (p < n && !(isSpaceChar(b))) {
            buf[p++] = (char) b;
            b = readByte();
        }
        return n == p ? buf : Arrays.copyOf(buf, p);
    }

    private char[][] nm(int n, int m) {
        char[][] map = new char[n][];
        for (int i = 0; i < n; i++) map[i] = ns(m);
        return map;
    }

    private int[] na(int n) {
        int[] a = new int[n];
        for (int i = 0; i < n; i++) a[i] = ni();
        return a;
    }

    private int ni() {
        int num = 0, b;
        boolean minus = false;
        while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
        if (b == '-') {
            minus = true;
            b = readByte();
        }

        while (true) {
            if (b >= '0' && b <= '9') {
                num = num * 10 + (b - '0');
            } else {
                return minus ? -num : num;
            }
            b = readByte();
        }
    }

    private long nl() {
        long num = 0;
        int b;
        boolean minus = false;
        while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
        if (b == '-') {
            minus = true;
            b = readByte();
        }

        while (true) {
            if (b >= '0' && b <= '9') {
                num = num * 10 + (b - '0');
            } else {
                return minus ? -num : num;
            }
            b = readByte();
        }
    }

    private void tr(Object...o) {
        if (INPUT.length() > 0) System.out.println(Arrays.deepToString(o));
    }
    void watch(Object...a) throws Exception {
        int i = 1;
        print("watch starts :");
        for (Object o: a) {
            //print(o);
            boolean notfound = true;
            if (o.getClass().isArray()) {

                String type = o.getClass().getName().toString();
                //print("type is "+type);
                switch (type) {
                    case "[I":
                        {
                            int[] test = (int[]) o;
                            print(i + " " + Arrays.toString(test));
                            break;
                        }
                    case "[[I":
                        {
                            int[][] obj = (int[][]) o;

                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[J":
                        {

                            long[] obj = (long[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[J":
                        {

                            long[][] obj = (long[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[D":
                        {

                            double[] obj = (double[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[D":
                        {

                            double[][] obj = (double[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[Ljava.lang.String":
                        {

                            String[] obj = (String[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[Ljava.lang.String":
                        {

                            String[][] obj = (String[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[C":
                        {
                            char[] obj = (char[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[C":
                        {

                            char[][] obj = (char[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }

                    default:
                        {
                            print(i + " type not identified");
                            break;
                        }
                }
                notfound = false;

            }
            if (o.getClass() == ArrayList.class) {
                print(i + " al: " + o);
                notfound = false;
            }
            if (o.getClass() == HashSet.class) {
                print(i + " hs: " + o);
                notfound = false;
            }
            if (o.getClass() == TreeSet.class) {
                print(i + " ts: " + o);
                notfound = false;
            }
            if (o.getClass() == TreeMap.class) {
                print(i + " tm: " + o);
                notfound = false;
            }
            if (o.getClass() == HashMap.class) {
                print(i + " hm: " + o);
                notfound = false;
            }
            if (o.getClass() == LinkedList.class) {
                print(i + " ll: " + o);
                notfound = false;
            }
            if (o.getClass() == PriorityQueue.class) {
                print(i + " pq : " + o);
                notfound = false;
            }
            if (o.getClass() == pair.class) {
                print(i + " pq : " + o);
                notfound = false;
            }

            if (notfound) {
                print(i + " unknown: " + o);
            }
            i++;
        }
        print("watch ends ");
    }

}


import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.*;
import java.io.*;
import java.math.*;
public class Main {
    InputStream is;
    PrintWriter out;
    String INPUT = "";
    //class  Declaration

    static class pair implements Comparable < pair > {
        long x;
        long y;

        pair(long i, long j) {
            x = i;
            y = j;

        }
        public int compareTo(pair p) {
            if (this.x != p.x) {
                return Long.compare(this.x,p.x);
            } else {
                return Long.compare(this.y,p.y);
            }
        }
        public int hashCode() {
            return (x + " " + y).hashCode();
        }
        public String toString() {
            return x + " " + y;
        }
        public boolean equals(Object o) {
            pair x = (pair) o;
            return (x.x == this.x && x.y == this.y);
        }
    }
    // int[] dx = {0,0,1,-1};
    // int[] dy = {1,-1,0,0};
    // int[] ddx = {0,0,1,-1,1,-1,1,-1};
    // int[] ddy = {1,-1,0,0,1,-1,-1,1};

int inf = (int) 1e9 + 9;
long biginf  = (long)1e17 + 7 ;
long mod =  (long)1e9 + 7;

void solve() throws Exception {
int n=ni();
Long[][] points = new Long[n][3];
TreeMap<Long,TreeMap<Long,TreeMap<Long,Integer>>> coord =new TreeMap<>();
boolean used[] = new boolean[n];
for(int i=0;i<n;++i){
    points[i][0] = nl();points[i][1] = nl();points[i][2] = nl();
    //final long[] fpoints = {points[i][0],points[i][1],points[i][1]};
    final int ind = i ;

    coord.compute(points[ind][2],(key,val)->{
        if(val==null){
            TreeMap<Long,TreeMap<Long,Integer>> ycor = new TreeMap<>();
            TreeMap<Long,Integer> xcor = new TreeMap<>();
            xcor.put(points[ind][0],ind);
            ycor.put(points[ind][1],xcor);
            return ycor;
        }
        else{

            TreeMap<Long,TreeMap<Long,Integer>> ycor= val ;
            ycor.compute(points[ind][1],(kk,xcor)->{
                if(xcor==null){
                    TreeMap<Long,Integer> xx = new TreeMap<>();
                    xx.put(points[ind][0],ind);
                    return xx;
                }
                else{
                    TreeMap<Long,Integer> xx = xcor;
                    xx.put(points[ind][0],ind);
                    return xx ;
                }
            });
            return ycor;
        }
    });
}
//pn(coord);

int[] ans = new int[n];
int ians = 0;
for(long z : coord.keySet()){
    TreeMap<Long,TreeMap<Long,Integer>> ycor = coord.get(z);
    for(long y: ycor.keySet()){
        TreeMap<Long,Integer> xcor = ycor.get(y);
        for(long x : xcor.keySet()){
            ans[ians++] = xcor.get(x);
        }
    }
}
for(int i=0;i<n/2;++i){
    pn((ans[i]+1)+" "+(ans[i+1]+1));
}

//pn(Arrays.deepToString(points));

}
long dist(Long[] a, Long[]b){
    long dis = 0;
    for(int i=0;i<3;++i){
        dis+= (a[i]-b[i])*(a[i]-b[i]);
    }
    return dis;
}

long pow(long a, long b) {
        long result = 1;
        while (b > 0) {
            if (b % 2 == 1) result = (result * a) % mod;
            b /= 2;
            a = (a * a) % mod;
        }
        return result;
    }

    void print(Object o) {
        System.out.println(o);
        System.out.flush();
    }

    long gcd(long a, long b) {
        if (b == 0)
            return a;
        return gcd(b, a % b);
    }
    void run() throws Exception {
        is = INPUT.isEmpty() ? System.in : new ByteArrayInputStream(INPUT.getBytes());
        out = new PrintWriter(System.out);

        long s = System.currentTimeMillis();
        solve();
        out.flush();
        if (!INPUT.isEmpty()) tr(System.currentTimeMillis() - s + "ms");
    }

    public static void main(String[] args) throws Exception {
        new Main().run();
    }

    //output methods
    private void pn(Object o) {
        out.println(o);
    }
    private void p(Object o) {
        out.print(o);
    }
    private ArrayList < ArrayList < Integer >> ng(int n, int e) {
        ArrayList < ArrayList < Integer >> g = new ArrayList < > ();
        for (int i = 0; i <= n; ++i) g.add(new ArrayList < > ());

        for (int i = 0; i < e; ++i) {
            int u = ni(), v = ni();
            g.get(u).add(v);
            g.get(v).add(u);
        }

        return g;
    }

    private ArrayList < ArrayList < pair >> nwg(int n, int e) {
        ArrayList < ArrayList < pair >> g = new ArrayList < > ();
        for (int i = 0; i <= n; ++i) g.add(new ArrayList < > ());

        for (int i = 0; i < e; ++i) {
            int u = ni(), v = ni(), w = ni();
            g.get(u).add(new pair(w, v));
            g.get(v).add(new pair(w, u));
        }

        return g;
    }

    //input methods
    private byte[] inbuf = new byte[1024];
    public int lenbuf = 0, ptrbuf = 0;

    private int readByte() {
        if (lenbuf == -1) throw new InputMismatchException();
        if (ptrbuf >= lenbuf) {
            ptrbuf = 0;
            try {
                lenbuf = is.read(inbuf);
            } catch (IOException e) {
                throw new InputMismatchException();
            }
            if (lenbuf <= 0) return -1;
        }
        return inbuf[ptrbuf++];
    }

    private boolean isSpaceChar(int c) {
        return !(c >= 33 && c <= 126);
    }
    private int skip() {
        int b;
        while ((b = readByte()) != -1 && isSpaceChar(b));
        return b;
    }

    private double nd() {
        return Double.parseDouble(ns());
    }
    private char nc() {
        return (char) skip();
    }

    private String ns() {
        int b = skip();
        StringBuilder sb = new StringBuilder();
        while (!(isSpaceChar(b))) { // when nextLine, (isSpaceChar(b) && b != ' ')
            sb.appendCodePoint(b);
            b = readByte();
        }
        return sb.toString();
    }

    private char[] ns(int n) {
        char[] buf = new char[n];
        int b = skip(), p = 0;
        while (p < n && !(isSpaceChar(b))) {
            buf[p++] = (char) b;
            b = readByte();
        }
        return n == p ? buf : Arrays.copyOf(buf, p);
    }

    private char[][] nm(int n, int m) {
        char[][] map = new char[n][];
        for (int i = 0; i < n; i++) map[i] = ns(m);
        return map;
    }

    private int[] na(int n) {
        int[] a = new int[n];
        for (int i = 0; i < n; i++) a[i] = ni();
        return a;
    }

    private int ni() {
        int num = 0, b;
        boolean minus = false;
        while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
        if (b == '-') {
            minus = true;
            b = readByte();
        }

        while (true) {
            if (b >= '0' && b <= '9') {
                num = num * 10 + (b - '0');
            } else {
                return minus ? -num : num;
            }
            b = readByte();
        }
    }

    private long nl() {
        long num = 0;
        int b;
        boolean minus = false;
        while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
        if (b == '-') {
            minus = true;
            b = readByte();
        }

        while (true) {
            if (b >= '0' && b <= '9') {
                num = num * 10 + (b - '0');
            } else {
                return minus ? -num : num;
            }
            b = readByte();
        }
    }

    private void tr(Object...o) {
        if (INPUT.length() > 0) System.out.println(Arrays.deepToString(o));
    }
    void watch(Object...a) throws Exception {
        int i = 1;
        print("watch starts :");
        for (Object o: a) {
            //print(o);
            boolean notfound = true;
            if (o.getClass().isArray()) {

                String type = o.getClass().getName().toString();
                //print("type is "+type);
                switch (type) {
                    case "[I":
                        {
                            int[] test = (int[]) o;
                            print(i + " " + Arrays.toString(test));
                            break;
                        }
                    case "[[I":
                        {
                            int[][] obj = (int[][]) o;

                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[J":
                        {

                            long[] obj = (long[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[J":
                        {

                            long[][] obj = (long[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[D":
                        {

                            double[] obj = (double[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[D":
                        {

                            double[][] obj = (double[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[Ljava.lang.String":
                        {

                            String[] obj = (String[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[Ljava.lang.String":
                        {

                            String[][] obj = (String[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }
                    case "[C":
                        {
                            char[] obj = (char[]) o;
                            print(i + " " + Arrays.toString(obj));
                            break;
                        }
                    case "[[C":
                        {

                            char[][] obj = (char[][]) o;
                            print(i + " " + Arrays.deepToString(obj));
                            break;
                        }

                    default:
                        {
                            print(i + " type not identified");
                            break;
                        }
                }
                notfound = false;

            }
            if (o.getClass() == ArrayList.class) {
                print(i + " al: " + o);
                notfound = false;
            }
            if (o.getClass() == HashSet.class) {
                print(i + " hs: " + o);
                notfound = false;
            }
            if (o.getClass() == TreeSet.class) {
                print(i + " ts: " + o);
                notfound = false;
            }
            if (o.getClass() == TreeMap.class) {
                print(i + " tm: " + o);
                notfound = false;
            }
            if (o.getClass() == HashMap.class) {
                print(i + " hm: " + o);
                notfound = false;
            }
            if (o.getClass() == LinkedList.class) {
                print(i + " ll: " + o);
                notfound = false;
            }
            if (o.getClass() == PriorityQueue.class) {
                print(i + " pq : " + o);
                notfound = false;
            }
            if (o.getClass() == pair.class) {
                print(i + " pq : " + o);
                notfound = false;
            }

            if (notfound) {
                print(i + " unknown: " + o);
            }
            i++;
        }
        print("watch ends ");
    }

}


import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class Main {
	public static void main(String args[])
	{
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		dataC str[]=new dataC[n+1];
		str[0]=new dataC();
		for(int i=1;i<=n;i++)
		{
			str[i]=new dataC();
			str[i].x=sc.nextInt();
			str[i].y=sc.nextInt();
			str[i].z=sc.nextInt();
			str[i].id=i;
		}
		Arrays.sort(str,1,n+1,new Comparator<dataC>()
				{
					public int compare(dataC a,dataC b)
					{
						if(a.x!=b.x) return a.x<b.x?-1:1;
						if(a.y!=b.y) return a.y<b.y?-1:1;
						return a.z<b.z?-1:1;
					}
				});
		boolean vis[]=new boolean[n+1];
		Arrays.fill(vis, false);
		for(int i=1;i<=n-1;i++)
		{
			if(str[i].x==str[i+1].x&&str[i].y==str[i+1].y)
			{
				vis[i]=true;vis[i+1]=true;
				System.out.println(str[i].id+" "+str[i+1].id);
				i++;
			}
		}
		for(int i=1;i<=n-1;i++)
		{
			if(vis[i]) continue;
			int j=i+1;
			while(j<=n&&vis[j]) j++;
			if(j<=n&&str[i].x==str[j].x)
			{
				vis[i]=true;vis[j]=true;
				System.out.println(str[i].id+" "+str[j].id);
				i=j;
			}
		}
		for(int i=1;i<=n-1;i++)
		{
			if(vis[i])
				continue;
			int j=i+1;
			while(vis[j])
				j++;
			System.out.println(str[i].id+" "+str[j].id);
			vis[i]=true;vis[j]=true;
			i=j;
		}
	}

}
class dataC
{
	int x,y,z,id;
}