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

class roads_as implements Runnable {
    class Point implements Comparable<Point> {
        int i;
        int x;
        int y;
        int d;

        Point(int i, int x, int y, int d) {
            this.i = i;
            this.x = x;
            this.y = y;
            this.d = d;
        }

        public int compareTo(Point o2) {
            Point o1 = this;
            if (o1.x < o2.x) {
                return -1;
            } else if (o1.x > o2.x) {
                return 1;
            } else if (o1.y < o2.y) {
                return -1;
            } else if (o1.y > o2.y) {
                return 1;
            } else {
                return 0;
            }
        }

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

    class Edge {
        int l;
        int r;

        Edge(int l, int r) {
            this.l = l;
            this.r = r;
        }
    }

    ArrayList<Point> upperConvexHull(ArrayList<Point> p) {
        final Point first = p.get(0);
        ArrayList<Point> q = new ArrayList<Point>();
        for (int i = 1; i < p.size(); i++) {
            q.add(p.get(i));
        }
        Collections.sort(q, new Comparator<Point>() {
            public int compare(Point o1, Point o2) {
                long v = 1L * (o1.x - first.x) * (o2.y - first.y) - 1L * (o2.x - first.x) * (o1.y - first.y);
                return v < 0 ? -1 : v > 0 ? 1 : 0;
            }
        });
        ArrayList<Point> hull = new ArrayList<Point>();
        hull.add(first);
        hull.add(q.get(0));
        for (int i = 1; i < q.size(); i++) {
            while (true) {
                Point prev = hull.get(hull.size() - 2);
                Point cur = hull.get(hull.size() - 1);
                Point pi = q.get(i);
                long v = 1L * (cur.x - prev.x) * (pi.y - prev.y) - 1L * (pi.x - prev.x) * (cur.y - prev.y);
                assert v != 0;
                if (v < 0) {
                    break;
                } else {
                    hull.remove(hull.size() - 1);
                }
            }
            hull.add(q.get(i));
            if (q.get(i) == p.get(p.size() - 1)) {
                break;
            }
        }
        return hull;
    }

    Set<Edge> build(ArrayList<Point> p) {
        if (p.size() == 1) {
            return new HashSet<Edge>();
        } else if (p.size() == 2) {
            Set<Edge> res = new HashSet<Edge>();
            assert p.get(0).d == 1;
            assert p.get(1).d == 1;
            res.add(new Edge(p.get(0).i, p.get(1).i));
            return res;
        }

        ArrayList<Point> ch = upperConvexHull(p);
        int hn = ch.size();
        boolean all1 = true;
        for (int i = 0; i < hn; i++) {
            Point pi = ch.get(i);
            if (pi.d != 1) {
                all1 = false;
            }
            for (int j = -1; j <= 1; j += 2) {
                if (i + j < 0 || i + j >= hn) continue;
                Point pn = ch.get(i + j);
                if (pi.d == 1 && pn.d != 1) {
                    ArrayList<Point> np = new ArrayList<Point>();
                    for (Point q : p) {
                        if (q != pi) {
                            np.add(q);
                            if (q == pn) {
                                q.d--;
                            }
                        }
                    }
                    Set<Edge> res = build(np);
                    res.add(new Edge(pi.i, pn.i));
                    return res;
                }
            }
        }

        if (all1) {
            if (p.get(1).d >= 2) {
                ArrayList<Point> np = new ArrayList<Point>();
                for (int i = 1; i < p.size(); i++) {
                    np.add(p.get(i));
                }
                np.get(0).d--;
                Set<Edge> res = build(np);
                res.add(new Edge(p.get(0).i, p.get(1).i));
                return res;
            }
            int s = -2;
            int k = -1;
            do {
                k++;
                s = s + 2 - p.get(k).d;
            } while (k < 1 || s >= 0);
            assert k < p.size() - 1;

            ArrayList<Point> lp = new ArrayList<Point>();
            for (int i = 0; i < k; i++) {
                lp.add(p.get(i));
            }
            Point lk = new Point(p.get(k).i, p.get(k).x, p.get(k).y, p.get(k).d + s);
            lp.add(lk);
            Set<Edge> res = build(lp);
            ArrayList<Point> rp = new ArrayList<Point>();
            for (int i = k + 1; i < p.size(); i++) {
                rp.add(p.get(i));
            }
            Point rk = new Point(p.get(k).i, p.get(k).x, p.get(k).y, -s);
            rp.add(rk);
            res.addAll(build(rp));
            return res;
        }

        int s = -2;
        int k = -1;
        do {
            k++;
            s = s + 2 - p.get(k).d;
        } while (s < -1);
        assert k < p.size() - 1;
        assert s == -1;

        int lh = 0;
        while (ch.get(lh + 1).compareTo(p.get(k)) <= 0) {
            lh++;
        }
        ch.get(lh).d--;
        ch.get(lh + 1).d--;

        ArrayList<Point> lp = new ArrayList<Point>();
        for (int i = 0; i <= k; i++) {
            lp.add(p.get(i));
        }
        Set<Edge> res = build(lp);
        ArrayList<Point> rp = new ArrayList<Point>();
        for (int i = k + 1; i < p.size(); i++) {
            rp.add(p.get(i));
        }
        res.addAll(build(rp));
        res.add(new Edge(ch.get(lh).i, ch.get(lh + 1).i));

        return res;

    }

    void assertNo3(ArrayList<Point> p) {
        for (int i = 0; i < p.size(); i++) {
            double[] d = new double[p.size() - 1];
            int k = 0;
            for (int j = 0; j < p.size(); j++) {
                if (i != j) {
                    d[k] = Math.atan2(p.get(j).y - p.get(i).y, p.get(j).x - p.get(i).x);
                    if (d[k] < 0) {
                        d[k] += Math.PI;
                    }
                    k++;
                }
            }
            Arrays.sort(d);
            for (int j = 0; j < d.length - 1; j++) {
                assert d[j + 1] - d[j] > 1e-9;
            }
        }
    }

    public void solve() throws IOException {
        Scanner in = new Scanner(new File("roads.in"));
        PrintWriter out = new PrintWriter(new File("roads.out"));

        int n = in.nextInt();
        ArrayList<Point> p = new ArrayList<Point>();
        int sum = 0;
        for (int i = 0; i < n; i++) {
            p.add(new Point(i + 1, in.nextInt(), in.nextInt(), in.nextInt()));
            sum += p.get(i).d;
        }
        assert sum == 2 * n - 2;

        assertNo3(p);

        Collections.sort(p);

        Set<Edge> ans = build(p);

        for (Edge e : ans) {
            out.println(e.l + " " + e.r);
        }

        in.close();
        out.close();
    }

    public void run() {
        try {
            solve();
        } catch (IOException e) {
            e.printStackTrace();
            throw new AssertionError();
        }
    }

    public static void main(String[] args) {
        new Thread(new roads_as()).start();
    }
}