#include<bits/stdc++.h>
using namespace std;
struct node{
    long double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
        a[i].x=a[i].x*cos(angle)-a[i].y*sin(angle);
        a[i].y=a[i].x*sin(angle)+a[i].y*cos(angle);
        a[i].x=a[i].x*cos(angle)-a[i].z*sin(angle);
        a[i].z=a[i].x*sin(angle)+a[i].z*cos(angle);
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
        long double mn=1e18;
        int tot=1000;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const long double eps = -1e10;
struct node{
    long double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
        long double ax=a[i].x,ay=a[i].y,az=a[i].z;
        ax=ax*cos(angle)-ay*sin(angle);
        ay=ax*sin(angle)+ay*cos(angle);
        a[i].x=ax;
        ax=ax*cos(angle)-az*sin(angle);
        az=ax*sin(angle)+az*cos(angle);
        a[i].x=a[i].y=ay,a[i].z=az;
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
        long double mn=1e18;
        int tot=1000;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
const int mod=1000000007;
int vis[N];
struct node{
    ll x,y,z,id;
    bool friend operator <(node a,node b){
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[N],b[N];
ll cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
pair<int,int>ans[N];
int main(){
    ios::sync_with_stdio(false), cin.tie(0);
    b[0].x=a[0].x=-1e9;
    int n,m,t,tot=0;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i;
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++) {
        if(a[i].x==a[i-1].x&&a[i].y==a[i-1].y) {
            vis[i]=vis[i-1]=1;
            ans[tot++]={a[i-1].id,a[i].id};
            i++;
        }
    }
    int l=0;
    for(int i=1;i<=n;i++) {
        if(!vis[i])b[++l]=a[i];
    }
    memset(vis,0,sizeof vis);
    for(int i=1;i<=l;i++) {
        if(b[i].x==b[i-1].x) {
            vis[i]=vis[i-1]=1;
            ans[tot++]={b[i-1].id,b[i].id};
            i++;
        }
    }
    l=0;
    for(int i=1;i<=n;i++) {
        if(!vis[i])a[++l]=b[i];
    }
    for(int i=2;i<=l;i+=2) {
        ans[tot++]={a[i-1].id,a[i].id};
    }
    for(int i=0;i<tot;i++) {
        printf("%d %d\n",ans[i].first,ans[i].second);
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
const long double eps = -1e10;
struct node{
    long double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
        //a[i].x=a[i].x*cos(angle)-a[i].y*sin(angle);
      //  a[i].y=a[i].x*sin(angle)+a[i].y*cos(angle);
        //a[i].x=a[i].x*cos(angle)-a[i].z*sin(angle);
        //a[i].z=a[i].x*sin(angle)+a[i].z*cos(angle);
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
        long double mn=1e18;
        int tot=1000;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)+eps<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const long double eps = -1e10;
struct node{
    long double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
        long double ax=a[i].x,ay=a[i].y,az=a[i].z;
        ax=a[i].x*cos(angle)-a[i].y*sin(angle);
        ay=a[i].x*sin(angle)+a[i].y*cos(angle);
        a[i].x=ax;
        ax=a[i].x*cos(angle)-a[i].z*sin(angle);
        az=a[i].x*sin(angle)+a[i].z*cos(angle);
        a[i].x=ax,a[i].y=ay,a[i].z=az;
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
        long double mn=1e18;
        int tot=6000;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const  double eps = -1e10;
struct node{
    double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%270)/180*PI;
    for(int i=1;i<=n;i++) {
         double ax=a[i].x,ay=a[i].y,az=a[i].z;
        ax=a[i].x*cos(angle)-a[i].y*sin(angle);
        ay=a[i].x*sin(angle)+a[i].y*cos(angle);
        a[i].x=ax;
        ax=a[i].x*cos(angle)-a[i].z*sin(angle);
        az=a[i].x*sin(angle)+a[i].z*cos(angle);
        a[i].x=ax,a[i].y=ay,a[i].z=az;
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
         double mn=1e18;
        int tot=2600;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const long double eps = -1e10;
struct node{
    long double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
        long double ax=a[i].x,ay=a[i].y,az=a[i].z;
        a[i].x=ax*cos(angle)-ay*sin(angle);
        a[i].y=ax*sin(angle)+ay*cos(angle);
        a[i].x=ax;
        a[i].x=ax*cos(angle)-az*sin(angle);
        a[i].z=ax*sin(angle)+az*cos(angle);
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
        long double mn=1e18;
        int tot=1000;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const  double eps = -1e10;
struct node{
    double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
         double ax=a[i].x,ay=a[i].y,az=a[i].z;
        ax=a[i].x*cos(angle)-a[i].y*sin(angle);
        ay=a[i].x*sin(angle)+a[i].y*cos(angle);
        a[i].x=ax;
        ax=a[i].x*cos(angle)-a[i].z*sin(angle);
        az=a[i].x*sin(angle)+a[i].z*cos(angle);
        a[i].x=ax,a[i].y=ay,a[i].z=az;
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
         double mn=1e18;
        int tot=2000;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const long double eps = -1e10;
struct node{
    long double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
        long double ax=a[i].x,ay=a[i].y,az=a[i].z;
        a[i].x=ax*cos(angle)-ay*sin(angle);
        a[i].y=ax*sin(angle)+ay*cos(angle);
        a[i].x=ax;
        a[i].x=ax*cos(angle)-az*sin(angle);
        a[i].z=ax*sin(angle)+az*cos(angle);
        a[i].y=ay,a[i].z=az;
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
        long double mn=1e18;
        int tot=1000;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
struct node{
    int x,y,z,id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z),a[i].id=i;
    sort(a+1,a+1+n );
    for(int i=1;i<=n/2;i++) {
        printf("%d %d\n",a[i*2-1].id,a[i*2].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const long double eps = -1e10;
struct node{
    long double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
        a[i].x=a[i].x*cos(angle)-a[i].y*sin(angle);
        a[i].y=a[i].x*sin(angle)+a[i].y*cos(angle);
        //a[i].x=a[i].x*cos(angle)-a[i].z*sin(angle);
        //a[i].z=a[i].x*sin(angle)+a[i].z*cos(angle);
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
        long double mn=1e18;
        int tot=1000;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)+eps<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
struct node{
    long long x,y,z,id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long long cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].z),a[i].id=i,s.insert(i);
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0,mn=1e18;
        for(auto i=s.begin();i!=s.end();i++) {
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%lld %lld\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const long double eps = -1e10;
struct node{
    long double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
        long double ax=a[i].x,ay=a[i].y,az=a[i].z;
        ax=ax*cos(angle)-ay*sin(angle);
        ay=ax*sin(angle)+ay*cos(angle);
        a[i].x=ax;
        ax=ax*cos(angle)-az*sin(angle);
        az=ax*sin(angle)+az*cos(angle);
        a[i].x=ax,a[i].y=ay,a[i].z=az;
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
        long double mn=1e18;
        int tot=1000;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const  double eps = -1e10;
struct node{
    double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 19260817;
    for(int i=1;i<=n;i++) {
         double ax=a[i].x,ay=a[i].y,az=a[i].z;
        ax=a[i].x*cos(angle)-a[i].y*sin(angle);
        ay=a[i].x*sin(angle)+a[i].y*cos(angle);
        a[i].x=ax;
        ax=a[i].x*cos(angle)-a[i].z*sin(angle);
        az=a[i].x*sin(angle)+a[i].z*cos(angle);
        a[i].x=ax,a[i].y=ay,a[i].z=az;
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
         double mn=1e18;
        int tot=2600;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
struct node{
    long double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
        a[i].x=a[i].x*cos(angle)-a[i].y*sin(angle);
        a[i].y=a[i].x*sin(angle)+a[i].y*cos(angle);
        //a[i].x=a[i].x*cos(angle)-a[i].z*sin(angle);
        //a[i].z=a[i].x*sin(angle)+a[i].z*cos(angle);
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
        long double mn=1e18;
        int tot=1000;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const  double eps = -1e10;
struct node{
    double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
         double ax=a[i].x,ay=a[i].y,az=a[i].z;
        ax=a[i].x*cos(angle)-a[i].y*sin(angle);
        ay=a[i].x*sin(angle)+a[i].y*cos(angle);
        a[i].x=ax;
        ax=a[i].x*cos(angle)-a[i].z*sin(angle);
        az=a[i].x*sin(angle)+a[i].z*cos(angle);
        a[i].x=ax,a[i].y=ay,a[i].z=az;
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
         double mn=1e18;
        int tot=2200;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const  double eps = -1e10;
struct node{
    double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
         double ax=a[i].x,ay=a[i].y,az=a[i].z;
        ax=a[i].x*cos(angle)-a[i].y*sin(angle);
        ay=a[i].x*sin(angle)+a[i].y*cos(angle);
        a[i].x=ax;
        ax=a[i].x*cos(angle)-a[i].z*sin(angle);
        az=a[i].x*sin(angle)+a[i].z*cos(angle);
        a[i].x=ax,a[i].y=ay,a[i].z=az;
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
         double mn=1e18;
        int tot=2600;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
const long double eps = -1e10;
struct node{
    long double x,y,z;
    int id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long double cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    cin>>a[i].x>>a[i].y>>a[i].z,a[i].id=i,s.insert(i);
    const double PI = acos(-1);
    double angle = 1.0*(rand()%180)/180*PI;
    for(int i=1;i<=n;i++) {
        a[i].x=a[i].x*cos(angle)-a[i].y*sin(angle);
        a[i].y=a[i].x*sin(angle)+a[i].y*cos(angle);
        //a[i].x=a[i].x*cos(angle)-a[i].z*sin(angle);
        //a[i].z=a[i].x*sin(angle)+a[i].z*cos(angle);
    }
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0;
        long double mn=1e18;
        int tot=1000;
        for(auto i=s.begin();i!=s.end();i++) {
            if(--tot==0)break;
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


#include<bits/stdc++.h>
using namespace std;
struct node{
    long long x,y,z,id;
    bool friend operator <(node a,node b) {
        return make_tuple(a.x,a.y,a.z)<make_tuple(b.x,b.y,b.z);
    }
}a[50001];
unordered_set<int>s;
long long cal(int id1,int id2) {
    return (a[id1].x-a[id2].x)*(a[id1].x-a[id2].x)+(a[id1].y-a[id2].y)*(a[id1].y-a[id2].y)+(a[id1].z-a[id2].z)*(a[id1].z-a[id2].z);
}
int vis[50001];
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].z),a[i].id=i,s.insert(i);
    sort(a+1,a+1+n );
    int cnt=n/2,pos=1;
    while(cnt--) {
        while(vis[pos])pos++;
        long long id=0,mn=1e18;
        for(auto i=s.begin();i!=s.end();i++) {
            int nowid=(*i);
            if(nowid==pos)continue;
            if(cal(nowid,pos)<mn) {
                id=nowid;
                mn=cal(nowid,pos);
            }
        }
        s.erase(id);
        s.erase(pos);
        vis[pos]=vis[id]=1;
        printf("%d %d\n",a[pos].id,a[id].id);
    }
}


//include <paBezanAshghal>
#include <bits/stdc++.h>
#define 	Time 	        	printf("\nTime : %.3lf s.\n", clock()*1.0/CLOCKS_PER_SEC)
#define 	For(J,R,K)       	for(int J=R;J<K;++J)
#define 	Rep(I,N)         	For(I,0,N)
#define 	MP 		        	make_pair
#define 	ALL(X)       		(X).begin(),(X).end()
#define 	SF 	        		scanf
#define 	PF 		        	printf
#define 	pii         		pair<long long,long long>
#define 	piii         		pair<  long long , pii  >
#define 	pdd             		pair<double , double>
#define 	Sort(v) 	        sort(ALL(v))
#define     GSORT(x)            sort(ALL(x), greater<typeof(*((x).begin()))>())
#define 	Test 		        freopen("a.in","r",stdin)
#define 	Testout 	        freopen("a.out","w",stdout)
#define     UNIQUE(v)           Sort(v); (v).reeize(unique(ALL(v)) - (v).begin())
#define 	pb 		        	push_back
#define 	Set(a,n)        	memset(a,n,sizeof(a))
#define 	MAXN 		        100000+99
#define 	EPS 		        1e-7
#define 	inf 		        1ll<<61
#define     SF                  scanf
#define     PF                  printf
#define     F                   first
#define     S                   second
#define 	vpii         		vector<pii>
#define 	vll        		    vector<ll>
#define     booste              ios_base::sync_with_stdio(NULL); cin.tie(NULL); cout.tie(NULL);
typedef long long ll;
typedef long double ld;
using namespace std;
inline ll get_num(char ch){
    if(ch == '-')return 0;
    else if(ch >= 'a' && ch <= 'z'){
        return 1 + (ch-'a');
    }
    else if(ch >= 'A' && ch <= 'Z'){
        return 27 +(ch - 'A');
    }
    else if(ch >= '0' && ch <= '9'){
        return 53 +(ch - '0');
    }
}
//inline ll pw(ll x ,ll y){
//    if(y==0) return 1;
//    ll p = pw(x ,y/2);
//    p*=p;
//    if(y&1) p*=x;
//    return p;
//}
 int dx[] = {1,0,-1, 0} , dy[] = {0,1, 0, -1};  // 4 Direction
//int dx[] = {1,0,-1, 0 , 2 ,0 ,-2, 0 , 3 ,0 ,-3 ,0} ,
 //   dy[] = {0,1, 0, -1 , 0 ,2 ,0 , -2 , 0 , 3, 0 , -3};  // 4 Direction

 //int dx[] = {1,-1,0,0,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1,1,-1}; // 8 Direction
/* int dx[] = {1,-1,1,-1,2,2,-2,-2} , dy[] = {2,2,-2,-2,1,-1,1,-1}; */ // Knight Direction
/* int dx[] = {2,-2,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1}; */ // Hexagonal Direction
inline int GA(ll n , vector<ll>&vec){
    Rep(i ,n){ll x ;cin>>x ; vec.push_back(x);}
    return 0;
}
inline int GAEG(ll m ,vector<ll>vec[]){
    Rep(i ,m){ll x  , y ;cin>>x>>y ; x-- , y-- ; vec[x].push_back(y); vec[y].push_back(x);}
    return 0;
}
const ll MD =  1000000007;

bool isvalid(ll xx, ll yy , ll n , ll m ){
    return (xx>=0 && xx<n && yy>=0 && yy<m);
}

ll pw(ll x, ll y, ll dr){
    if(y==0) return 1;
    ll o = y/2;
    ll f = pw(x, o , dr)%dr;
    if(y%2){
        return (((f*f)%dr)*x)%dr;
    }
    else return (f*f)%dr;
}

struct st{
    ll x, y, z;
    ll ind;

    bool operator < (st &b){
        return (b.x==x) ? ((b.y==y) ? (z<b.z) :  (y<b.y)) : (x<b.x);
    }

};

bool cmp (const st &a , const st &b){
            return (b.y==a.y) ? ((b.x==a.x) ? (a.z<b.z) :  (a.x<b.x)) : (a.y<b.y);
}
vector<st>v;
int main(){
//    Test;
    ll n ;cin>>n;
    Rep(i , n){
        ll x , y, z;

        cin>>x>>y>>z;
        st q;
        q.ind=i+1;
        q.x=x;
        q.y =y;
        q.z = z;
        v.push_back(q);
    }
    Sort(v);
    vll del;
    Rep(i ,v.size()){
        ll j = i+1;
        while(j<v.size() && v[j].x==v[j-1].x && v[j].y == v[j-1].y) {
                cout<<v[j-1].ind<<" " <<v[j].ind<<endl;
                del.push_back(j-1);
                del.push_back(j);
            j+=2;
        }
        i= j-1;
    }
    vector<st> oo ;
    Sort(del);
    Rep(i , v.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) oo.push_back(v[i]);
    }
    del.clear();
     Rep(i ,oo.size()){
        ll j = i+1;
        while(j<oo.size() && oo[j].x==oo[j-1].x) {
                cout<<oo[j-1].ind<<" " <<oo[j].ind<<endl;
                del.push_back(j-1);
                del.push_back(j);

            j+=2;
        }
        i= j-1;
    }
    v.clear();
    Rep(i , oo.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) v.push_back(oo[i]);
    }

    for(int i = 1; i<v.size();i+=2)            cout<<v[i-1].ind<<" " <<v[i].ind<<endl;

    return 0;
}


//include <paBezanAshghal>
#include <bits/stdc++.h>
#define 	Time 	        	printf("\nTime : %.3lf s.\n", clock()*1.0/CLOCKS_PER_SEC)
#define 	For(J,R,K)       	for(int J=R;J<K;++J)
#define 	Rep(I,N)         	For(I,0,N)
#define 	MP 		        	make_pair
#define 	ALL(X)       		(X).begin(),(X).end()
#define 	SF 	        		scanf
#define 	PF 		        	printf
#define 	pii         		pair<long long,long long>
#define 	piii         		pair<  long long , pii  >
#define 	pdd             		pair<double , double>
#define 	Sort(v) 	        sort(ALL(v))
#define     GSORT(x)            sort(ALL(x), greater<typeof(*((x).begin()))>())
#define 	Test 		        freopen("a.in","r",stdin)
#define 	Testout 	        freopen("a.out","w",stdout)
#define     UNIQUE(v)           Sort(v); (v).reeize(unique(ALL(v)) - (v).begin())
#define 	pb 		        	push_back
#define 	Set(a,n)        	memset(a,n,sizeof(a))
#define 	MAXN 		        100000+99
#define 	EPS 		        1e-7
#define 	inf 		        1ll<<61
#define     SF                  scanf
#define     PF                  printf
#define     F                   first
#define     S                   second
#define 	vpii         		vector<pii>
#define 	vll        		    vector<ll>
#define     booste              ios_base::sync_with_stdio(NULL); cin.tie(NULL); cout.tie(NULL);
typedef long long ll;
typedef long double ld;
using namespace std;
inline ll get_num(char ch){
    if(ch == '-')return 0;
    else if(ch >= 'a' && ch <= 'z'){
        return 1 + (ch-'a');
    }
    else if(ch >= 'A' && ch <= 'Z'){
        return 27 +(ch - 'A');
    }
    else if(ch >= '0' && ch <= '9'){
        return 53 +(ch - '0');
    }
}
//inline ll pw(ll x ,ll y){
//    if(y==0) return 1;
//    ll p = pw(x ,y/2);
//    p*=p;
//    if(y&1) p*=x;
//    return p;
//}
 int dx[] = {1,0,-1, 0} , dy[] = {0,1, 0, -1};  // 4 Direction
//int dx[] = {1,0,-1, 0 , 2 ,0 ,-2, 0 , 3 ,0 ,-3 ,0} ,
 //   dy[] = {0,1, 0, -1 , 0 ,2 ,0 , -2 , 0 , 3, 0 , -3};  // 4 Direction

 //int dx[] = {1,-1,0,0,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1,1,-1}; // 8 Direction
/* int dx[] = {1,-1,1,-1,2,2,-2,-2} , dy[] = {2,2,-2,-2,1,-1,1,-1}; */ // Knight Direction
/* int dx[] = {2,-2,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1}; */ // Hexagonal Direction
inline int GA(ll n , vector<ll>&vec){
    Rep(i ,n){ll x ;cin>>x ; vec.push_back(x);}
    return 0;
}
inline int GAEG(ll m ,vector<ll>vec[]){
    Rep(i ,m){ll x  , y ;cin>>x>>y ; x-- , y-- ; vec[x].push_back(y); vec[y].push_back(x);}
    return 0;
}
const ll MD =  1000000007;

bool isvalid(ll xx, ll yy , ll n , ll m ){
    return (xx>=0 && xx<n && yy>=0 && yy<m);
}

ll pw(ll x, ll y, ll dr){
    if(y==0) return 1;
    ll o = y/2;
    ll f = pw(x, o , dr)%dr;
    if(y%2){
        return (((f*f)%dr)*x)%dr;
    }
    else return (f*f)%dr;
}

struct st{
    ll x, y, z;
    ll ind;

    bool operator < (st &b){
        return (b.x==x) ? ((b.y==y) ? (z<b.z) :  (y<b.y)) : (x<b.x);
    }

};
vector<st>v;
int main(){
//    Test;
    ll n ;cin>>n;
    Rep(i , n){
        ll x , y, z;

        cin>>x>>y>>z;
        st q;
        q.ind=i+1;
        q.x=x;
        q.y =y;
        q.z = z;
        v.push_back(q);
    }
    Sort(v);
    vll del;
    Rep(i ,v.size()){
        ll j = i+1;
        while(j<v.size() && v[j].x==v[j-1].x && v[j].y == v[j-1].y) {
            cout<<v[j-1].ind<<" " <<v[j].ind<<endl;
            del.push_back(j-1);
            del.push_back(j);
            j++;
        }
        i= j;
    }
    vector<st> oo ;
    Sort(del);
    Rep(i , v.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) oo.push_back(v[i]);
    }
    del.clear();
     Rep(i ,oo.size()){
        ll j = i+1;
        while(j<oo.size() && oo[j].x==oo[j-1].x) {
            cout<<oo[j-1].ind<<" " <<oo[j].ind<<endl;
            del.push_back(j-1);
            del.push_back(j);
            j++;
        }
        i= j;
    }
    v.clear();
    Rep(i , oo.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) v.push_back(oo[i]);
    }
    for(int i = 1; i<v.size();i+=2)            cout<<v[i-1].ind<<" " <<v[i].ind<<endl;

    return 0;
}


//include <paBezanAshghal>
#include <bits/stdc++.h>
#define 	Time 	        	printf("\nTime : %.3lf s.\n", clock()*1.0/CLOCKS_PER_SEC)
#define 	For(J,R,K)       	for(int J=R;J<K;++J)
#define 	Rep(I,N)         	For(I,0,N)
#define 	MP 		        	make_pair
#define 	ALL(X)       		(X).begin(),(X).end()
#define 	SF 	        		scanf
#define 	PF 		        	printf
#define 	pii         		pair<long long,long long>
#define 	piii         		pair<  long long , pii  >
#define 	pdd             		pair<double , double>
#define 	Sort(v) 	        sort(ALL(v))
#define     GSORT(x)            sort(ALL(x), greater<typeof(*((x).begin()))>())
#define 	Test 		        freopen("a.in","r",stdin)
#define 	Testout 	        freopen("a.out","w",stdout)
#define     UNIQUE(v)           Sort(v); (v).reeize(unique(ALL(v)) - (v).begin())
#define 	pb 		        	push_back
#define 	Set(a,n)        	memset(a,n,sizeof(a))
#define 	MAXN 		        100000+99
#define 	EPS 		        1e-7
#define 	inf 		        1ll<<61
#define     SF                  scanf
#define     PF                  printf
#define     F                   first
#define     S                   second
#define 	vpii         		vector<pii>
#define 	vll        		    vector<ll>
#define     booste              ios_base::sync_with_stdio(NULL); cin.tie(NULL); cout.tie(NULL);
typedef long long ll;
typedef long double ld;
using namespace std;
inline ll get_num(char ch){
    if(ch == '-')return 0;
    else if(ch >= 'a' && ch <= 'z'){
        return 1 + (ch-'a');
    }
    else if(ch >= 'A' && ch <= 'Z'){
        return 27 +(ch - 'A');
    }
    else if(ch >= '0' && ch <= '9'){
        return 53 +(ch - '0');
    }
}
//inline ll pw(ll x ,ll y){
//    if(y==0) return 1;
//    ll p = pw(x ,y/2);
//    p*=p;
//    if(y&1) p*=x;
//    return p;
//}
 int dx[] = {1,0,-1, 0} , dy[] = {0,1, 0, -1};  // 4 Direction
//int dx[] = {1,0,-1, 0 , 2 ,0 ,-2, 0 , 3 ,0 ,-3 ,0} ,
 //   dy[] = {0,1, 0, -1 , 0 ,2 ,0 , -2 , 0 , 3, 0 , -3};  // 4 Direction

 //int dx[] = {1,-1,0,0,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1,1,-1}; // 8 Direction
/* int dx[] = {1,-1,1,-1,2,2,-2,-2} , dy[] = {2,2,-2,-2,1,-1,1,-1}; */ // Knight Direction
/* int dx[] = {2,-2,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1}; */ // Hexagonal Direction
inline int GA(ll n , vector<ll>&vec){
    Rep(i ,n){ll x ;cin>>x ; vec.push_back(x);}
    return 0;
}
inline int GAEG(ll m ,vector<ll>vec[]){
    Rep(i ,m){ll x  , y ;cin>>x>>y ; x-- , y-- ; vec[x].push_back(y); vec[y].push_back(x);}
    return 0;
}
const ll MD =  1000000007;

bool isvalid(ll xx, ll yy , ll n , ll m ){
    return (xx>=0 && xx<n && yy>=0 && yy<m);
}

ll pw(ll x, ll y, ll dr){
    if(y==0) return 1;
    ll o = y/2;
    ll f = pw(x, o , dr)%dr;
    if(y%2){
        return (((f*f)%dr)*x)%dr;
    }
    else return (f*f)%dr;
}

struct st{
    ll x, y, z;
    ll ind;

    bool operator < (st &b){
        return (b.x==x) ? ((b.y==y) ? (z<b.z) :  (y<b.y)) : (x<b.x);
    }

};
vector<st>v;
int main(){
//    Test;
    ll n ;cin>>n;
    Rep(i , n){
        ll x , y, z;

        cin>>x>>y>>z;
        st q;
        q.ind=i+1;
        q.x=x;
        q.y =y;
        q.z = z;
        v.push_back(q);
    }
    Sort(v);
    vll del;
    Rep(i ,v.size()){
        ll j = i+1;
        ll nm = 0;
        while(j<v.size() && v[j].x==v[j-1].x && v[j].y == v[j-1].y) {
            nm++;
            if(nm&1){
                cout<<v[j-1].ind<<" " <<v[j].ind<<endl;
                del.push_back(j-1);
                del.push_back(j);
            }
            j++;
        }
        i= j;
    }
    vector<st> oo ;
    Sort(del);
    Rep(i , v.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) oo.push_back(v[i]);
    }
    del.clear();
     Rep(i ,oo.size()){
        ll j = i+1;
        ll nm= 0;
        while(j<oo.size() && oo[j].x==oo[j-1].x) {
            nm++;
            if(nm&1){
                cout<<oo[j-1].ind<<" " <<oo[j].ind<<endl;
                del.push_back(j-1);
                del.push_back(j);
            }
            j++;
        }
        i= j;
    }
    v.clear();
    Rep(i , oo.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) v.push_back(oo[i]);
    }
    for(int i = 1; i<v.size();i+=2)            cout<<v[i-1].ind<<" " <<v[i].ind<<endl;

    return 0;
}


//include <paBezanAshghal>
#include <bits/stdc++.h>
#define 	Time 	        	printf("\nTime : %.3lf s.\n", clock()*1.0/CLOCKS_PER_SEC)
#define 	For(J,R,K)       	for(int J=R;J<K;++J)
#define 	Rep(I,N)         	For(I,0,N)
#define 	MP 		        	make_pair
#define 	ALL(X)       		(X).begin(),(X).end()
#define 	SF 	        		scanf
#define 	PF 		        	printf
#define 	pii         		pair<long long,long long>
#define 	piii         		pair<  long long , pii  >
#define 	pdd             		pair<double , double>
#define 	Sort(v) 	        sort(ALL(v))
#define     GSORT(x)            sort(ALL(x), greater<typeof(*((x).begin()))>())
#define 	Test 		        freopen("a.in","r",stdin)
#define 	Testout 	        freopen("a.out","w",stdout)
#define     UNIQUE(v)           Sort(v); (v).reeize(unique(ALL(v)) - (v).begin())
#define 	pb 		        	push_back
#define 	Set(a,n)        	memset(a,n,sizeof(a))
#define 	MAXN 		        100000+99
#define 	EPS 		        1e-7
#define 	inf 		        1ll<<61
#define     SF                  scanf
#define     PF                  printf
#define     F                   first
#define     S                   second
#define 	vpii         		vector<pii>
#define 	vll        		    vector<ll>
#define     booste              ios_base::sync_with_stdio(NULL); cin.tie(NULL); cout.tie(NULL);
typedef long long ll;
typedef long double ld;
using namespace std;
inline ll get_num(char ch){
    if(ch == '-')return 0;
    else if(ch >= 'a' && ch <= 'z'){
        return 1 + (ch-'a');
    }
    else if(ch >= 'A' && ch <= 'Z'){
        return 27 +(ch - 'A');
    }
    else if(ch >= '0' && ch <= '9'){
        return 53 +(ch - '0');
    }
}
//inline ll pw(ll x ,ll y){
//    if(y==0) return 1;
//    ll p = pw(x ,y/2);
//    p*=p;
//    if(y&1) p*=x;
//    return p;
//}
 int dx[] = {1,0,-1, 0} , dy[] = {0,1, 0, -1};  // 4 Direction
//int dx[] = {1,0,-1, 0 , 2 ,0 ,-2, 0 , 3 ,0 ,-3 ,0} ,
 //   dy[] = {0,1, 0, -1 , 0 ,2 ,0 , -2 , 0 , 3, 0 , -3};  // 4 Direction

 //int dx[] = {1,-1,0,0,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1,1,-1}; // 8 Direction
/* int dx[] = {1,-1,1,-1,2,2,-2,-2} , dy[] = {2,2,-2,-2,1,-1,1,-1}; */ // Knight Direction
/* int dx[] = {2,-2,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1}; */ // Hexagonal Direction
inline int GA(ll n , vector<ll>&vec){
    Rep(i ,n){ll x ;cin>>x ; vec.push_back(x);}
    return 0;
}
inline int GAEG(ll m ,vector<ll>vec[]){
    Rep(i ,m){ll x  , y ;cin>>x>>y ; x-- , y-- ; vec[x].push_back(y); vec[y].push_back(x);}
    return 0;
}
const ll MD =  1000000007;

bool isvalid(ll xx, ll yy , ll n , ll m ){
    return (xx>=0 && xx<n && yy>=0 && yy<m);
}

ll pw(ll x, ll y, ll dr){
    if(y==0) return 1;
    ll o = y/2;
    ll f = pw(x, o , dr)%dr;
    if(y%2){
        return (((f*f)%dr)*x)%dr;
    }
    else return (f*f)%dr;
}

struct st{
    ll x, y, z;
    ll ind;

    bool operator < (st &b){
        return (b.x==x) ? ((b.y==y) ? (z<b.z) :  (y<b.y)) : (x<b.x);
    }

};

bool cmp (const st &a , const st &b){
            return (b.y==a.y) ? ((b.x==a.x) ? (a.z<b.z) :  (a.x<b.x)) : (a.y<b.y);
}
vector<st>v;
int main(){
//    Test;
    ll n ;cin>>n;
    Rep(i , n){
        ll x , y, z;

        cin>>x>>y>>z;
        st q;
        q.ind=i+1;
        q.x=x;
        q.y =y;
        q.z = z;
        v.push_back(q);
    }
    Sort(v);
    vll del;
    Rep(i ,v.size()){
        ll j = i+1;
        ll nm = 0;
        while(j<v.size() && v[j].x==v[j-1].x && v[j].y == v[j-1].y) {
            nm++;
            if(nm&1){
                cout<<v[j-1].ind<<" " <<v[j].ind<<endl;
                del.push_back(j-1);
                del.push_back(j);
            }
            j++;
        }
        i= j-1;
    }
    vector<st> oo ;
    Sort(del);
    Rep(i , v.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) oo.push_back(v[i]);
    }
    del.clear();
     Rep(i ,oo.size()){
        ll j = i+1;
        ll nm= 0;
        while(j<oo.size() && oo[j].x==oo[j-1].x) {
            nm++;
            if(nm&1){
                cout<<oo[j-1].ind<<" " <<oo[j].ind<<endl;
                del.push_back(j-1);
                del.push_back(j);
            }
            j++;
        }
        i= j-1;
    }
    v.clear();
    Rep(i , oo.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) v.push_back(oo[i]);
    }

    for(int i = 1; i<v.size();i+=2)            cout<<v[i-1].ind<<" " <<v[i].ind<<endl;

    return 0;
}


//include <paBezanAshghal>
#include <bits/stdc++.h>
#define 	Time 	        	printf("\nTime : %.3lf s.\n", clock()*1.0/CLOCKS_PER_SEC)
#define 	For(J,R,K)       	for(int J=R;J<K;++J)
#define 	Rep(I,N)         	For(I,0,N)
#define 	MP 		        	make_pair
#define 	ALL(X)       		(X).begin(),(X).end()
#define 	SF 	        		scanf
#define 	PF 		        	printf
#define 	pii         		pair<long long,long long>
#define 	piii         		pair<  long long , pii  >
#define 	pdd             		pair<double , double>
#define 	Sort(v) 	        sort(ALL(v))
#define     GSORT(x)            sort(ALL(x), greater<typeof(*((x).begin()))>())
#define 	Test 		        freopen("a.in","r",stdin)
#define 	Testout 	        freopen("a.out","w",stdout)
#define     UNIQUE(v)           Sort(v); (v).reeize(unique(ALL(v)) - (v).begin())
#define 	pb 		        	push_back
#define 	Set(a,n)        	memset(a,n,sizeof(a))
#define 	MAXN 		        100000+99
#define 	EPS 		        1e-7
#define 	inf 		        1ll<<61
#define     SF                  scanf
#define     PF                  printf
#define     F                   first
#define     S                   second
#define 	vpii         		vector<pii>
#define 	vll        		    vector<ll>
#define     booste              ios_base::sync_with_stdio(NULL); cin.tie(NULL); cout.tie(NULL);
typedef long long ll;
typedef long double ld;
using namespace std;
inline ll get_num(char ch){
    if(ch == '-')return 0;
    else if(ch >= 'a' && ch <= 'z'){
        return 1 + (ch-'a');
    }
    else if(ch >= 'A' && ch <= 'Z'){
        return 27 +(ch - 'A');
    }
    else if(ch >= '0' && ch <= '9'){
        return 53 +(ch - '0');
    }
}
//inline ll pw(ll x ,ll y){
//    if(y==0) return 1;
//    ll p = pw(x ,y/2);
//    p*=p;
//    if(y&1) p*=x;
//    return p;
//}
 int dx[] = {1,0,-1, 0} , dy[] = {0,1, 0, -1};  // 4 Direction
//int dx[] = {1,0,-1, 0 , 2 ,0 ,-2, 0 , 3 ,0 ,-3 ,0} ,
 //   dy[] = {0,1, 0, -1 , 0 ,2 ,0 , -2 , 0 , 3, 0 , -3};  // 4 Direction

 //int dx[] = {1,-1,0,0,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1,1,-1}; // 8 Direction
/* int dx[] = {1,-1,1,-1,2,2,-2,-2} , dy[] = {2,2,-2,-2,1,-1,1,-1}; */ // Knight Direction
/* int dx[] = {2,-2,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1}; */ // Hexagonal Direction
inline int GA(ll n , vector<ll>&vec){
    Rep(i ,n){ll x ;cin>>x ; vec.push_back(x);}
    return 0;
}
inline int GAEG(ll m ,vector<ll>vec[]){
    Rep(i ,m){ll x  , y ;cin>>x>>y ; x-- , y-- ; vec[x].push_back(y); vec[y].push_back(x);}
    return 0;
}
const ll MD =  1000000007;

bool isvalid(ll xx, ll yy , ll n , ll m ){
    return (xx>=0 && xx<n && yy>=0 && yy<m);
}

ll pw(ll x, ll y, ll dr){
    if(y==0) return 1;
    ll o = y/2;
    ll f = pw(x, o , dr)%dr;
    if(y%2){
        return (((f*f)%dr)*x)%dr;
    }
    else return (f*f)%dr;
}

struct st{
    ll x, y, z;
    ll ind;

    bool operator < (st &b){
        return (b.x==x) ? ((b.y==y) ? (z<b.z) :  (y<b.y)) : (x<b.x);
    }

};

bool cmp (const st &a , const st &b){
            return (b.y==a.y) ? ((b.x==a.x) ? (a.z<b.z) :  (a.x<b.x)) : (a.y<b.y);
}
vector<st>v;
int main(){
//    Test;
    ll n ;cin>>n;
    Rep(i , n){
        ll x , y, z;

        cin>>x>>y>>z;
        st q;
        q.ind=i+1;
        q.x=x;
        q.y =y;
        q.z = z;
        v.push_back(q);
    }
    Sort(v);
    vll del;
    Rep(i ,v.size()){
        ll j = i+1;
        ll nm = 0;
        while(j<v.size() && v[j].x==v[j-1].x && v[j].y == v[j-1].y) {
            nm++;
            if(nm&1){
                cout<<v[j-1].ind<<" " <<v[j].ind<<endl;
                del.push_back(j-1);
                del.push_back(j);
            }
            j++;
        }
        i= j-1;
    }
    vector<st> oo ;
    Sort(del);
    Rep(i , v.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) oo.push_back(v[i]);
    }
    del.clear();
     Rep(i ,oo.size()){
        ll j = i+1;
        ll nm= 0;
        while(j<oo.size() && oo[j].x==oo[j-1].x) {
            nm++;
            if(nm&1){
                cout<<oo[j-1].ind<<" " <<oo[j].ind<<endl;
                del.push_back(j-1);
                del.push_back(j);
            }
            j++;
        }
        i= j-1;
    }
    v.clear();
    Rep(i , oo.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) v.push_back(oo[i]);
    }
    del.clear();
    sort(v.begin() , v.end() , cmp);
        Rep(i ,v.size()){
        ll j = i+1;
        ll nm= 0;
        while(j<v.size() && v[j].y==v[j-1].y) {
            nm++;
            if(nm&1){
                cout<<v[j-1].ind<<" " <<v[j].ind<<endl;
                del.push_back(j-1);
                del.push_back(j);
            }
            j++;
        }
        i= j-1;
    }
    oo.clear();
    Rep(i , v.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) oo.push_back(v[i]);
    }
    for(int i = 1; i<oo.size();i+=2)            cout<<oo[i-1].ind<<" " <<oo[i].ind<<endl;

    return 0;
}


//include <paBezanAshghal>
#include <bits/stdc++.h>
#define 	Time 	        	printf("\nTime : %.3lf s.\n", clock()*1.0/CLOCKS_PER_SEC)
#define 	For(J,R,K)       	for(int J=R;J<K;++J)
#define 	Rep(I,N)         	For(I,0,N)
#define 	MP 		        	make_pair
#define 	ALL(X)       		(X).begin(),(X).end()
#define 	SF 	        		scanf
#define 	PF 		        	printf
#define 	pii         		pair<long long,long long>
#define 	piii         		pair<  long long , pii  >
#define 	pdd             		pair<double , double>
#define 	Sort(v) 	        sort(ALL(v))
#define     GSORT(x)            sort(ALL(x), greater<typeof(*((x).begin()))>())
#define 	Test 		        freopen("a.in","r",stdin)
#define 	Testout 	        freopen("a.out","w",stdout)
#define     UNIQUE(v)           Sort(v); (v).reeize(unique(ALL(v)) - (v).begin())
#define 	pb 		        	push_back
#define 	Set(a,n)        	memset(a,n,sizeof(a))
#define 	MAXN 		        100000+99
#define 	EPS 		        1e-7
#define 	inf 		        1ll<<61
#define     SF                  scanf
#define     PF                  printf
#define     F                   first
#define     S                   second
#define 	vpii         		vector<pii>
#define 	vll        		    vector<ll>
#define     booste              ios_base::sync_with_stdio(NULL); cin.tie(NULL); cout.tie(NULL);
typedef long long ll;
typedef long double ld;
using namespace std;
inline ll get_num(char ch){
    if(ch == '-')return 0;
    else if(ch >= 'a' && ch <= 'z'){
        return 1 + (ch-'a');
    }
    else if(ch >= 'A' && ch <= 'Z'){
        return 27 +(ch - 'A');
    }
    else if(ch >= '0' && ch <= '9'){
        return 53 +(ch - '0');
    }
}
//inline ll pw(ll x ,ll y){
//    if(y==0) return 1;
//    ll p = pw(x ,y/2);
//    p*=p;
//    if(y&1) p*=x;
//    return p;
//}
 int dx[] = {1,0,-1, 0} , dy[] = {0,1, 0, -1};  // 4 Direction
//int dx[] = {1,0,-1, 0 , 2 ,0 ,-2, 0 , 3 ,0 ,-3 ,0} ,
 //   dy[] = {0,1, 0, -1 , 0 ,2 ,0 , -2 , 0 , 3, 0 , -3};  // 4 Direction

 //int dx[] = {1,-1,0,0,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1,1,-1}; // 8 Direction
/* int dx[] = {1,-1,1,-1,2,2,-2,-2} , dy[] = {2,2,-2,-2,1,-1,1,-1}; */ // Knight Direction
/* int dx[] = {2,-2,1,1,-1,-1} , dy[] = {0,0,1,-1,1,-1}; */ // Hexagonal Direction
inline int GA(ll n , vector<ll>&vec){
    Rep(i ,n){ll x ;cin>>x ; vec.push_back(x);}
    return 0;
}
inline int GAEG(ll m ,vector<ll>vec[]){
    Rep(i ,m){ll x  , y ;cin>>x>>y ; x-- , y-- ; vec[x].push_back(y); vec[y].push_back(x);}
    return 0;
}
const ll MD =  1000000007;

bool isvalid(ll xx, ll yy , ll n , ll m ){
    return (xx>=0 && xx<n && yy>=0 && yy<m);
}

ll pw(ll x, ll y, ll dr){
    if(y==0) return 1;
    ll o = y/2;
    ll f = pw(x, o , dr)%dr;
    if(y%2){
        return (((f*f)%dr)*x)%dr;
    }
    else return (f*f)%dr;
}

struct st{
    ll x, y, z;
    ll ind;

    bool operator < (st &b){
        return (b.x==x) ? ((b.y==y) ? (z<b.z) :  (y<b.y)) : (x<b.x);
    }

};
vector<st>v;
int main(){
//    Test;
    ll n ;cin>>n;
    Rep(i , n){
        ll x , y, z;

        cin>>x>>y>>z;
        st q;
        q.ind=i+1;
        q.x=x;
        q.y =y;
        q.z = z;
        v.push_back(q);
    }
    Sort(v);
    vll del;
    Rep(i ,v.size()){
        ll j = i+1;
        while(j<v.size() && v[j].x==v[j-1].x && v[j].y == v[j-1].y) {
            cout<<v[j-1].ind<<" " <<v[j].ind<<endl;
            del.push_back(j-1);
            del.push_back(j);
            j+=2;
        }
        i= j;
    }
    vector<st> oo ;
    Sort(del);
    Rep(i , v.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) oo.push_back(v[i]);
    }
    del.clear();
     Rep(i ,oo.size()){
        ll j = i+1;
        while(j<oo.size() && oo[j].x==oo[j-1].x) {
            cout<<oo[j-1].ind<<" " <<oo[j].ind<<endl;
            del.push_back(j-1);
            del.push_back(j);
            j+=2;
        }
        i= j;
    }
    v.clear();
    Rep(i , oo.size()){
        bool bl = binary_search(del.begin() , del.end() , i);
        if(!bl) v.push_back(oo[i]);
    }
    for(int i = 1; i<v.size();i+=2)            cout<<v[i-1].ind<<" " <<v[i].ind<<endl;

    return 0;
}


#include <bits/stdc++.h>
using namespace std;

#define pb(x) push_back(x)
#define memset0(x) memset((x), 0, sizeof(x))
#define memsetM1(x) memset((x), -1, sizeof(x))

#define F first
#define S second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef tuple<int, int, int, int> Tiii;
#define UNIQUE(v) (v).erase(unique((v).begin(), (v).end()), (v).end());
#define all(x) (x).begin(), (x).end()
#define precise(a) fixed<<setprecision(a)
#define isvowel(c) c=='a' || c=='e' || c=='i' || c=='o' || c=='u'

#define SQ(x) ((x)*(x))
#define issq(x) (((ll)(sqrt((x))))*((ll)(sqrt((x))))==(x))
#define mem(a,x) memset(a,x,sizeof(a))
#define inf 1e18
#define lg(r,n) (int)(log2(n)/log2(r))
#define fast ios_base::sync_with_stdio(false)
#define one(x) __builtin_popcount(x)

//#define iceil(n, x) (((n) + (x) - 1) / (x))
//return the minimum number >=n , which is divisible by x
//int next_popcount(int n){int c = (n & -n);int r = n+c;int p = r^n;int x = (p>>2)/c;return x  r;}
// Let, n e k ti onBit ache, this func return a serial combination of k onBit.

//struct BIT{
//    vector<ll> Tree;ll Size=0;void init(int n){Tree.assign(n+1,0);Size=n;}
//    void update(int indx,ll val){while(indx<=n){Tree[indx] += val;indx += (indx)&(-indx);}}
//    ll query(int indx){ll sum=0;while(indx>0)
//        {sum += Tree[indx];sum %= mod;indx -= (indx)&(-indx);}return sum;}
//    ll query(int l, int r){return (query(r) - query(l-1)+mod)%mod;}
//};
//ll bigmod(ll a, ll b){if(b==0) return 1%mod;ll x = bigmod(a,b/2);
//    x = (x*x)%mod;if(b%2==1) x = (x*a)%mod;return x;}
//ll egcd(ll a, ll b, ll &x, ll &y){if(a==0){x=0;y=1;return b;}
//    ll x1,y1;ll d =egcd(b%a, a,x1,y1);x = y1-(b/a)*x1;y=x1;return d;}

//have to check;
#define lcase(a) (char)tolower(a)
#define ucase(a) (char)toupper(a)
#define section(a,b,w) set_intersection(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define union(a,b,w) set_union(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define copy(a,w) copy(a.begin(), a.end(),back_inserter(w));
//

constexpr double PI = (double)acos(-1);
constexpr double EPS = (double)1e-10;
constexpr int dx[] = {0,1,0,-1};
constexpr int dy[] = {1,0,-1,0};
constexpr int dxx[] = {-1, 0, 0, 1, 0, -1, -1, 1, 1};
constexpr int dyy[] = {0, -1, 1, 0, 0, 1, -1, 1, -1};
inline int toInt(string s) { int v; istringstream sin(s); sin >> v; return v; }

#define bit(n) (1LL << (n))
#define bitset(a, b) (a) |= (1 << (b))
#define bitunset(a, b) (a) &= ~(1 << (b))
#define bitcheck(a, b) ((((a) >> (b)) & 1) == 1)
#define bitcount(a) __builtin_popcountll((a))
inline void bin(ll n){if (n > 1)bin(n>>1); cout<<(n&1);}
inline int binlen(ll x) { if(x==0) return 1; return floor(log2(double(x)))+1;}
#define FRO freopen("in.txt","r",stdin);
#define oFRO freopen("out.txt","w",stdout);
#define DEBUG(args...) do {cerr<<#args<<' '; print(args);} while(0);cerr<<endl;
template<typename T> void print(const T& v){cerr<<v<<' ';}
template<typename T1,typename... T2>
void print(const T1& first,const T2&... rest){print(first);print(rest...);}
template<class T>constexpr const T&_min(const T&x,const T&y){return x<y?x:y;}template<class T>constexpr const T&_max(const T&x,const T&y){return x<y?y:x;}
template<class T,class...Ts>constexpr const T&_min(const T&x,const Ts&...xs){return _min(x,_min(xs...));}
template<class T,class...Ts>constexpr const T&_max(const T&x,const Ts&...xs){return _max(x,_max(xs...));}
template<class T,class...Ts>void MIN(T&x,const Ts&...xs){x=_min(x,xs...);}template<class T,class...Ts>void MAX(T&x,const Ts&...xs){x=_max(x,xs...);}
#define min(...) _min(__VA_ARGS__)
#define max(...) _max(__VA_ARGS__)

//bool chkbnd(int i,int j) {return 0<=i && i<n && 0<=j && j<m;}

ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
// ==============================================================================================

vector<tuple<int,int,int,int>>vec;
bool flag[2001];

bool cmp1(Tiii A, Tiii B)
{
        return get<0>(A)<get<0>(B);
}
bool cmp2(Tiii A, Tiii B)
{
        return get<1>(A)<get<1>(B);
}
bool cmp3(Tiii A, Tiii B)
{
        return get<2>(A)<get<2>(B);
}
vector<pii>ans;

class solution{
public:

        int n,m,i,j,a,b,c;
        void solve()
        {
                cin>>n;
                for(i=1;i<=n;i++)
                {
                        cin>>a>>b>>c;
                        Tiii tmp(a,b,c,i);
                        vec.push_back(tmp);
                }

                sort(all(vec),cmp1);
                int down;
                for(i=0;i<n;i++)
                {
                        down=i;
                        while(down+1<n && get<0>(vec[i]) == get<0>(vec[down+1]) )down++;
                        sort(vec.begin()+i, vec.begin()+down,cmp2);

                        int  jdown;
                        for(j=i;j<=down;j++)
                        {
                                jdown=j;
                                while(jdown+1<=down && get<1>(vec[j]) == get<1>(vec[jdown+1]) ) jdown++;
                                sort(vec.begin()+j,vec.begin()+jdown, cmp3  );

                                for(int k=j+1;k<=jdown;k+=2)
                                {
                                        ans.push_back({ get<3>(vec[k-1]), get<3>(vec[k]) });
                                        flag[k-1]=1;
                                        flag[k]=1;
                                }

                                j=jdown;
                        }
                        int cnt=0,a;
                        for(int k=i;k<=down;k++)
                        {
                                if(flag[k]==0)
                                {
                                        cnt++;
                                        if(cnt==1)
                                                a=k;
                                        else
                                        {
                                                cnt=0;
                                                ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                                flag[a]=1;
                                                flag[k]=1;
                                        }
                                }
                        }

                        i=down;
                }
                int cnt=0;
                for(int k=0;k<n;k++)
                {
                        if(flag[k]==0)
                        {
                                cnt++;
                                if(cnt==1)
                                        a=k;
                                else
                                {
                                        cnt=0;
                                        ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                        flag[a]=1;
                                        flag[k]=1;
                                }
                        }
                }
                for(auto x: ans)
                {
                        cout<<x.first<<' '<<x.second<<endl;
                }

        }
        ///---->make all int ll must<----
};

int main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        solution ob;
        ob.solve();
	return 0;
}


#include <bits/stdc++.h>
using namespace std;

#define pb(x) push_back(x)
#define memset0(x) memset((x), 0, sizeof(x))
#define memsetM1(x) memset((x), -1, sizeof(x))

#define F first
#define S second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef tuple<int, int, int, int> Tiii;
#define UNIQUE(v) (v).erase(unique((v).begin(), (v).end()), (v).end());
#define all(x) (x).begin(), (x).end()
#define precise(a) fixed<<setprecision(a)
#define isvowel(c) c=='a' || c=='e' || c=='i' || c=='o' || c=='u'

#define SQ(x) ((x)*(x))
#define issq(x) (((ll)(sqrt((x))))*((ll)(sqrt((x))))==(x))
#define mem(a,x) memset(a,x,sizeof(a))
#define inf 1e18
#define lg(r,n) (int)(log2(n)/log2(r))
#define fast ios_base::sync_with_stdio(false)
#define one(x) __builtin_popcount(x)

//#define iceil(n, x) (((n) + (x) - 1) / (x))
//return the minimum number >=n , which is divisible by x
//int next_popcount(int n){int c = (n & -n);int r = n+c;int p = r^n;int x = (p>>2)/c;return x  r;}
// Let, n e k ti onBit ache, this func return a serial combination of k onBit.

//struct BIT{
//    vector<ll> Tree;ll Size=0;void init(int n){Tree.assign(n+1,0);Size=n;}
//    void update(int indx,ll val){while(indx<=n){Tree[indx] += val;indx += (indx)&(-indx);}}
//    ll query(int indx){ll sum=0;while(indx>0)
//        {sum += Tree[indx];sum %= mod;indx -= (indx)&(-indx);}return sum;}
//    ll query(int l, int r){return (query(r) - query(l-1)+mod)%mod;}
//};
//ll bigmod(ll a, ll b){if(b==0) return 1%mod;ll x = bigmod(a,b/2);
//    x = (x*x)%mod;if(b%2==1) x = (x*a)%mod;return x;}
//ll egcd(ll a, ll b, ll &x, ll &y){if(a==0){x=0;y=1;return b;}
//    ll x1,y1;ll d =egcd(b%a, a,x1,y1);x = y1-(b/a)*x1;y=x1;return d;}

//have to check;
#define lcase(a) (char)tolower(a)
#define ucase(a) (char)toupper(a)
#define section(a,b,w) set_intersection(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define union(a,b,w) set_union(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define copy(a,w) copy(a.begin(), a.end(),back_inserter(w));
//

constexpr double PI = (double)acos(-1);
constexpr double EPS = (double)1e-10;
constexpr int dx[] = {0,1,0,-1};
constexpr int dy[] = {1,0,-1,0};
constexpr int dxx[] = {-1, 0, 0, 1, 0, -1, -1, 1, 1};
constexpr int dyy[] = {0, -1, 1, 0, 0, 1, -1, 1, -1};
inline int toInt(string s) { int v; istringstream sin(s); sin >> v; return v; }

#define bit(n) (1LL << (n))
#define bitset(a, b) (a) |= (1 << (b))
#define bitunset(a, b) (a) &= ~(1 << (b))
#define bitcheck(a, b) ((((a) >> (b)) & 1) == 1)
#define bitcount(a) __builtin_popcountll((a))
inline void bin(ll n){if (n > 1)bin(n>>1); cout<<(n&1);}
inline int binlen(ll x) { if(x==0) return 1; return floor(log2(double(x)))+1;}
#define FRO freopen("in.txt","r",stdin);
#define oFRO freopen("out.txt","w",stdout);
#define DEBUG(args...) do {cerr<<#args<<' '; print(args);} while(0);cerr<<endl;
template<typename T> void print(const T& v){cerr<<v<<' ';}
template<typename T1,typename... T2>
void print(const T1& first,const T2&... rest){print(first);print(rest...);}
template<class T>constexpr const T&_min(const T&x,const T&y){return x<y?x:y;}template<class T>constexpr const T&_max(const T&x,const T&y){return x<y?y:x;}
template<class T,class...Ts>constexpr const T&_min(const T&x,const Ts&...xs){return _min(x,_min(xs...));}
template<class T,class...Ts>constexpr const T&_max(const T&x,const Ts&...xs){return _max(x,_max(xs...));}
template<class T,class...Ts>void MIN(T&x,const Ts&...xs){x=_min(x,xs...);}template<class T,class...Ts>void MAX(T&x,const Ts&...xs){x=_max(x,xs...);}
#define min(...) _min(__VA_ARGS__)
#define max(...) _max(__VA_ARGS__)

//bool chkbnd(int i,int j) {return 0<=i && i<n && 0<=j && j<m;}

ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
// ==============================================================================================

vector<tuple<int,int,int,int>>vec;
bool flag[2001];

bool cmp1(Tiii A, Tiii B)
{
        return get<0>(A)<get<0>(B);
}
bool cmp2(Tiii A, Tiii B)
{
        return get<1>(A)<get<1>(B);
}
bool cmp3(Tiii A, Tiii B)
{
        return get<2>(A)<get<2>(B);
}
vector<pii>ans;

class solution{
public:

        int n,m,i,j,a,b,c;
        void solve()
        {
                cin>>n;
                for(i=1;i<=n;i++)
                {
                        cin>>a>>b>>c;
                        Tiii tmp(a,b,c,i);
                        vec.push_back(tmp);
                }

                sort(all(vec),cmp1);
                int up=1,down=1;
                for(i=0;i<n;i++)
                {
                        down=i;
                        while(down+1<n && get<0>(vec[i]) == get<0>(vec[down+1]) )down++;
                        sort(vec.begin()+i, vec.begin()+down,cmp2);

                        int j=i, jdown=i;
                        for(j=i;j<=down;j++)
                        {
                                jdown=j;
                                while(jdown+1<down && get<1>(vec[j]) == get<1>(vec[jdown+1]) ) jdown++;
                                sort(vec.begin()+j,vec.begin()+jdown, cmp3  );

                                for(int k=j+1;k<=jdown;k+=2)
                                {
                                        ans.push_back({ get<3>(vec[k-1]), get<3>(vec[k]) });
                                        flag[k-1]=1;
                                        flag[k]=1;
                                }

                                j=jdown;
                        }
                        int cnt=0,a;
                        for(int k=i;k<=down;k++)
                        {
                                if(flag[k]==0)
                                {
                                        cnt++;
                                        if(cnt==1)
                                                a=k;
                                        else
                                        {
                                                cnt=0;
                                                ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                                flag[a]=1;
                                                flag[k]=1;
                                        }
                                }
                        }

                        i=down;
                }
                int cnt=0;
                for(int k=0;k<n;k++)
                {
                        if(flag[k]==0)
                        {
                                cnt++;
                                if(cnt==1)
                                        a=k;
                                else
                                {
                                        cnt=0;
                                        ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                        flag[a]=1;
                                        flag[k]=1;
                                }
                        }
                }
                for(auto x: ans)
                {
                        cout<<x.first<<' '<<x.second<<endl;
                }

        }
        ///---->make all int ll must<----
};

int main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        solution ob;
        ob.solve();
	return 0;
}


#include <bits/stdc++.h>
using namespace std;

#define pb(x) push_back(x)
#define memset0(x) memset((x), 0, sizeof(x))
#define memsetM1(x) memset((x), -1, sizeof(x))

#define F first
#define S second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef tuple<int, int, int, int> Tiii;
#define UNIQUE(v) (v).erase(unique((v).begin(), (v).end()), (v).end());
#define all(x) (x).begin(), (x).end()
#define precise(a) fixed<<setprecision(a)
#define isvowel(c) c=='a' || c=='e' || c=='i' || c=='o' || c=='u'

#define SQ(x) ((x)*(x))
#define issq(x) (((ll)(sqrt((x))))*((ll)(sqrt((x))))==(x))
#define mem(a,x) memset(a,x,sizeof(a))
#define inf 1e18
#define lg(r,n) (int)(log2(n)/log2(r))
#define fast ios_base::sync_with_stdio(false)
#define one(x) __builtin_popcount(x)

//#define iceil(n, x) (((n) + (x) - 1) / (x))
//return the minimum number >=n , which is divisible by x
//int next_popcount(int n){int c = (n & -n);int r = n+c;int p = r^n;int x = (p>>2)/c;return x  r;}
// Let, n e k ti onBit ache, this func return a serial combination of k onBit.

//struct BIT{
//    vector<ll> Tree;ll Size=0;void init(int n){Tree.assign(n+1,0);Size=n;}
//    void update(int indx,ll val){while(indx<=n){Tree[indx] += val;indx += (indx)&(-indx);}}
//    ll query(int indx){ll sum=0;while(indx>0)
//        {sum += Tree[indx];sum %= mod;indx -= (indx)&(-indx);}return sum;}
//    ll query(int l, int r){return (query(r) - query(l-1)+mod)%mod;}
//};
//ll bigmod(ll a, ll b){if(b==0) return 1%mod;ll x = bigmod(a,b/2);
//    x = (x*x)%mod;if(b%2==1) x = (x*a)%mod;return x;}
//ll egcd(ll a, ll b, ll &x, ll &y){if(a==0){x=0;y=1;return b;}
//    ll x1,y1;ll d =egcd(b%a, a,x1,y1);x = y1-(b/a)*x1;y=x1;return d;}

//have to check;
#define lcase(a) (char)tolower(a)
#define ucase(a) (char)toupper(a)
#define section(a,b,w) set_intersection(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define union(a,b,w) set_union(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define copy(a,w) copy(a.begin(), a.end(),back_inserter(w));
//

constexpr double PI = (double)acos(-1);
constexpr double EPS = (double)1e-10;
constexpr int dx[] = {0,1,0,-1};
constexpr int dy[] = {1,0,-1,0};
constexpr int dxx[] = {-1, 0, 0, 1, 0, -1, -1, 1, 1};
constexpr int dyy[] = {0, -1, 1, 0, 0, 1, -1, 1, -1};
inline int toInt(string s) { int v; istringstream sin(s); sin >> v; return v; }

#define bit(n) (1LL << (n))
#define bitset(a, b) (a) |= (1 << (b))
#define bitunset(a, b) (a) &= ~(1 << (b))
#define bitcheck(a, b) ((((a) >> (b)) & 1) == 1)
#define bitcount(a) __builtin_popcountll((a))
inline void bin(ll n){if (n > 1)bin(n>>1); cout<<(n&1);}
inline int binlen(ll x) { if(x==0) return 1; return floor(log2(double(x)))+1;}
#define FRO freopen("in.txt","r",stdin);
#define oFRO freopen("out.txt","w",stdout);
#define DEBUG(args...) do {cerr<<#args<<' '; print(args);} while(0);cerr<<endl;
template<typename T> void print(const T& v){cerr<<v<<' ';}
template<typename T1,typename... T2>
void print(const T1& first,const T2&... rest){print(first);print(rest...);}
template<class T>constexpr const T&_min(const T&x,const T&y){return x<y?x:y;}template<class T>constexpr const T&_max(const T&x,const T&y){return x<y?y:x;}
template<class T,class...Ts>constexpr const T&_min(const T&x,const Ts&...xs){return _min(x,_min(xs...));}
template<class T,class...Ts>constexpr const T&_max(const T&x,const Ts&...xs){return _max(x,_max(xs...));}
template<class T,class...Ts>void MIN(T&x,const Ts&...xs){x=_min(x,xs...);}template<class T,class...Ts>void MAX(T&x,const Ts&...xs){x=_max(x,xs...);}
#define min(...) _min(__VA_ARGS__)
#define max(...) _max(__VA_ARGS__)

//bool chkbnd(int i,int j) {return 0<=i && i<n && 0<=j && j<m;}

ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
// ==============================================================================================

vector<tuple<int,int,int,int>>vec;
bool flag[50005];

bool cmp1(Tiii A, Tiii B)
{
        return get<0>(A)<get<0>(B);
}
bool cmp2(Tiii A, Tiii B)
{
        return get<1>(A)<get<1>(B);
}
bool cmp3(Tiii A, Tiii B)
{
        return get<2>(A)<get<2>(B);
}
vector<pii>ans;

class solution{
public:

        int n,m,i,j,a,b,c;
        void solve()
        {
                cin>>n;
                for(i=1;i<=n;i++)
                {
                        cin>>a>>b>>c;
                        Tiii tmp(a,b,c,i);
                        vec.push_back(tmp);
                }

                sort(all(vec),cmp1);
                int down;
                for(i=0;i<n;i++)
                {
                        down=i;
                        while(down+1<n && get<0>(vec[i]) == get<0>(vec[down+1]) )down++;
                        sort(vec.begin()+i, vec.begin()+down+1,cmp2);

                        int  jdown;
                        for(j=i;j<=down;j++)
                        {
                                jdown=j;
                                while(jdown+1<=down && get<1>(vec[j]) == get<1>(vec[jdown+1]) ) jdown++;
                                sort(vec.begin()+j,vec.begin()+jdown+1, cmp3  );

                                for(int k=j+1;k<=jdown;k+=2)
                                {
                                        ans.push_back({ get<3>(vec[k-1]), get<3>(vec[k]) });
                                        flag[k-1]=1;
                                        flag[k]=1;
                                }

                                j=jdown;
                        }
                        int cnt=0,a;
                        for(int k=i;k<=down;k++)
                        {
                                if(flag[k]==0)
                                {
                                        cnt++;
                                        if(cnt==1)
                                                a=k;
                                        else
                                        {
                                                cnt=0;
                                                ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                                flag[a]=1;
                                                flag[k]=1;
                                        }
                                }
                        }

                        i=down;
                }
                int cnt=0;
                for(int k=0;k<n;k++)
                {
                        if(flag[k]==0)
                        {
                                cnt++;
                                if(cnt==1)
                                        a=k;
                                else
                                {
                                        cnt=0;
                                        ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                        flag[a]=1;
                                        flag[k]=1;
                                }
                        }
                }
                for(auto x: ans)
                {
                        cout<<x.first<<' '<<x.second<<endl;
                }

        }
        ///---->make all int ll must<----
};

int main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        solution ob;
        ob.solve();
	return 0;
}


#include <bits/stdc++.h>
using namespace std;

#define pb(x) push_back(x)
#define memset0(x) memset((x), 0, sizeof(x))
#define memsetM1(x) memset((x), -1, sizeof(x))

#define F first
#define S second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef tuple<int, int, int, int> Tiii;
#define UNIQUE(v) (v).erase(unique((v).begin(), (v).end()), (v).end());
#define all(x) (x).begin(), (x).end()
#define precise(a) fixed<<setprecision(a)
#define isvowel(c) c=='a' || c=='e' || c=='i' || c=='o' || c=='u'

#define SQ(x) ((x)*(x))
#define issq(x) (((ll)(sqrt((x))))*((ll)(sqrt((x))))==(x))
#define mem(a,x) memset(a,x,sizeof(a))
#define inf 1e18
#define lg(r,n) (int)(log2(n)/log2(r))
#define fast ios_base::sync_with_stdio(false)
#define one(x) __builtin_popcount(x)

//#define iceil(n, x) (((n) + (x) - 1) / (x))
//return the minimum number >=n , which is divisible by x
//int next_popcount(int n){int c = (n & -n);int r = n+c;int p = r^n;int x = (p>>2)/c;return x  r;}
// Let, n e k ti onBit ache, this func return a serial combination of k onBit.

//struct BIT{
//    vector<ll> Tree;ll Size=0;void init(int n){Tree.assign(n+1,0);Size=n;}
//    void update(int indx,ll val){while(indx<=n){Tree[indx] += val;indx += (indx)&(-indx);}}
//    ll query(int indx){ll sum=0;while(indx>0)
//        {sum += Tree[indx];sum %= mod;indx -= (indx)&(-indx);}return sum;}
//    ll query(int l, int r){return (query(r) - query(l-1)+mod)%mod;}
//};
//ll bigmod(ll a, ll b){if(b==0) return 1%mod;ll x = bigmod(a,b/2);
//    x = (x*x)%mod;if(b%2==1) x = (x*a)%mod;return x;}
//ll egcd(ll a, ll b, ll &x, ll &y){if(a==0){x=0;y=1;return b;}
//    ll x1,y1;ll d =egcd(b%a, a,x1,y1);x = y1-(b/a)*x1;y=x1;return d;}

//have to check;
#define lcase(a) (char)tolower(a)
#define ucase(a) (char)toupper(a)
#define section(a,b,w) set_intersection(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define union(a,b,w) set_union(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define copy(a,w) copy(a.begin(), a.end(),back_inserter(w));
//

constexpr double PI = (double)acos(-1);
constexpr double EPS = (double)1e-10;
constexpr int dx[] = {0,1,0,-1};
constexpr int dy[] = {1,0,-1,0};
constexpr int dxx[] = {-1, 0, 0, 1, 0, -1, -1, 1, 1};
constexpr int dyy[] = {0, -1, 1, 0, 0, 1, -1, 1, -1};
inline int toInt(string s) { int v; istringstream sin(s); sin >> v; return v; }

#define bit(n) (1LL << (n))
#define bitset(a, b) (a) |= (1 << (b))
#define bitunset(a, b) (a) &= ~(1 << (b))
#define bitcheck(a, b) ((((a) >> (b)) & 1) == 1)
#define bitcount(a) __builtin_popcountll((a))
inline void bin(ll n){if (n > 1)bin(n>>1); cout<<(n&1);}
inline int binlen(ll x) { if(x==0) return 1; return floor(log2(double(x)))+1;}
#define FRO freopen("in.txt","r",stdin);
#define oFRO freopen("out.txt","w",stdout);
#define DEBUG(args...) do {cerr<<#args<<' '; print(args);} while(0);cerr<<endl;
template<typename T> void print(const T& v){cerr<<v<<' ';}
template<typename T1,typename... T2>
void print(const T1& first,const T2&... rest){print(first);print(rest...);}
template<class T>constexpr const T&_min(const T&x,const T&y){return x<y?x:y;}template<class T>constexpr const T&_max(const T&x,const T&y){return x<y?y:x;}
template<class T,class...Ts>constexpr const T&_min(const T&x,const Ts&...xs){return _min(x,_min(xs...));}
template<class T,class...Ts>constexpr const T&_max(const T&x,const Ts&...xs){return _max(x,_max(xs...));}
template<class T,class...Ts>void MIN(T&x,const Ts&...xs){x=_min(x,xs...);}template<class T,class...Ts>void MAX(T&x,const Ts&...xs){x=_max(x,xs...);}
#define min(...) _min(__VA_ARGS__)
#define max(...) _max(__VA_ARGS__)

//bool chkbnd(int i,int j) {return 0<=i && i<n && 0<=j && j<m;}

ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
// ==============================================================================================

vector<tuple<int,int,int,int>>vec;
bool flag[2001];

bool cmp1(Tiii A, Tiii B)
{
        return get<0>(A)<get<0>(B);
}
bool cmp2(Tiii A, Tiii B)
{
        return get<1>(A)<get<1>(B);
}
bool cmp3(Tiii A, Tiii B)
{
        return get<2>(A)<get<2>(B);
}
vector<pii>ans;

class solution{
public:

        int n,m,i,j,a,b,c;
        void solve()
        {
                cin>>n;
                for(i=1;i<=n;i++)
                {
                        cin>>a>>b>>c;
                        Tiii tmp(a,b,c,i);
                        vec.push_back(tmp);
                }

                sort(all(vec),cmp1);
                int up=1,down=1;
                for(i=0;i<n;i++)
                {
                        down=i;
                        while(down+1<n && get<0>(vec[i]) == get<0>(vec[down+1]) )down++;
                        sort(vec.begin()+i, vec.begin()+down+1,cmp2);

                        int j=i, jdown=i;
                        for(j=i;j<=down;j++)
                        {
                                jdown=j;
                                while(jdown+1<down && get<1>(vec[j]) == get<1>(vec[jdown+1]) ) jdown++;
                                sort(vec.begin()+j,vec.begin()+jdown+1, cmp3  );

                                for(int k=j+1;k<=jdown;k+=2)
                                {
                                        ans.push_back({ get<3>(vec[k-1]), get<3>(vec[k]) });
                                        flag[k-1]=1;
                                        flag[k]=1;
                                }

                                j=jdown;
                        }
                        int cnt=0,a;
                        for(int k=i;k<=down;k++)
                        {
                                if(flag[k]==0)
                                {
                                        cnt++;
                                        if(cnt==1)
                                                a=k;
                                        else
                                        {
                                                cnt=0;
                                                ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                                flag[a]=1;
                                                flag[k]=1;
                                        }
                                }
                        }

                        i=down;
                }
                int cnt=0;
                for(int k=0;k<n;k++)
                {
                        if(flag[k]==0)
                        {
                                cnt++;
                                if(cnt==1)
                                        a=k;
                                else
                                {
                                        cnt=0;
                                        ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                        flag[a]=1;
                                        flag[k]=1;
                                }
                        }
                }
                for(auto x: ans)
                {
                        cout<<x.first<<' '<<x.second<<endl;
                }

        }
        ///---->make all int ll must<----
};

int main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        solution ob;
        ob.solve();
	return 0;
}


#include <bits/stdc++.h>
using namespace std;

#define pb(x) push_back(x)
#define memset0(x) memset((x), 0, sizeof(x))
#define memsetM1(x) memset((x), -1, sizeof(x))

#define F first
#define S second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef tuple<int, int, int, int> Tiii;
#define UNIQUE(v) (v).erase(unique((v).begin(), (v).end()), (v).end());
#define all(x) (x).begin(), (x).end()
#define precise(a) fixed<<setprecision(a)
#define isvowel(c) c=='a' || c=='e' || c=='i' || c=='o' || c=='u'

#define SQ(x) ((x)*(x))
#define issq(x) (((ll)(sqrt((x))))*((ll)(sqrt((x))))==(x))
#define mem(a,x) memset(a,x,sizeof(a))
#define inf 1e18
#define lg(r,n) (int)(log2(n)/log2(r))
#define fast ios_base::sync_with_stdio(false)
#define one(x) __builtin_popcount(x)

//#define iceil(n, x) (((n) + (x) - 1) / (x))
//return the minimum number >=n , which is divisible by x
//int next_popcount(int n){int c = (n & -n);int r = n+c;int p = r^n;int x = (p>>2)/c;return x  r;}
// Let, n e k ti onBit ache, this func return a serial combination of k onBit.

//struct BIT{
//    vector<ll> Tree;ll Size=0;void init(int n){Tree.assign(n+1,0);Size=n;}
//    void update(int indx,ll val){while(indx<=n){Tree[indx] += val;indx += (indx)&(-indx);}}
//    ll query(int indx){ll sum=0;while(indx>0)
//        {sum += Tree[indx];sum %= mod;indx -= (indx)&(-indx);}return sum;}
//    ll query(int l, int r){return (query(r) - query(l-1)+mod)%mod;}
//};
//ll bigmod(ll a, ll b){if(b==0) return 1%mod;ll x = bigmod(a,b/2);
//    x = (x*x)%mod;if(b%2==1) x = (x*a)%mod;return x;}
//ll egcd(ll a, ll b, ll &x, ll &y){if(a==0){x=0;y=1;return b;}
//    ll x1,y1;ll d =egcd(b%a, a,x1,y1);x = y1-(b/a)*x1;y=x1;return d;}

//have to check;
#define lcase(a) (char)tolower(a)
#define ucase(a) (char)toupper(a)
#define section(a,b,w) set_intersection(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define union(a,b,w) set_union(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define copy(a,w) copy(a.begin(), a.end(),back_inserter(w));
//

constexpr double PI = (double)acos(-1);
constexpr double EPS = (double)1e-10;
constexpr int dx[] = {0,1,0,-1};
constexpr int dy[] = {1,0,-1,0};
constexpr int dxx[] = {-1, 0, 0, 1, 0, -1, -1, 1, 1};
constexpr int dyy[] = {0, -1, 1, 0, 0, 1, -1, 1, -1};
inline int toInt(string s) { int v; istringstream sin(s); sin >> v; return v; }

#define bit(n) (1LL << (n))
#define bitset(a, b) (a) |= (1 << (b))
#define bitunset(a, b) (a) &= ~(1 << (b))
#define bitcheck(a, b) ((((a) >> (b)) & 1) == 1)
#define bitcount(a) __builtin_popcountll((a))
inline void bin(ll n){if (n > 1)bin(n>>1); cout<<(n&1);}
inline int binlen(ll x) { if(x==0) return 1; return floor(log2(double(x)))+1;}
#define FRO freopen("in.txt","r",stdin);
#define oFRO freopen("out.txt","w",stdout);
#define DEBUG(args...) do {cerr<<#args<<' '; print(args);} while(0);cerr<<endl;
template<typename T> void print(const T& v){cerr<<v<<' ';}
template<typename T1,typename... T2>
void print(const T1& first,const T2&... rest){print(first);print(rest...);}
template<class T>constexpr const T&_min(const T&x,const T&y){return x<y?x:y;}template<class T>constexpr const T&_max(const T&x,const T&y){return x<y?y:x;}
template<class T,class...Ts>constexpr const T&_min(const T&x,const Ts&...xs){return _min(x,_min(xs...));}
template<class T,class...Ts>constexpr const T&_max(const T&x,const Ts&...xs){return _max(x,_max(xs...));}
template<class T,class...Ts>void MIN(T&x,const Ts&...xs){x=_min(x,xs...);}template<class T,class...Ts>void MAX(T&x,const Ts&...xs){x=_max(x,xs...);}
#define min(...) _min(__VA_ARGS__)
#define max(...) _max(__VA_ARGS__)

//bool chkbnd(int i,int j) {return 0<=i && i<n && 0<=j && j<m;}

ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
// ==============================================================================================

vector<tuple<int,int,int,int>>vec;
bool flag[2001];

bool cmp1(Tiii A, Tiii B)
{
        return get<0>(A)<get<0>(B);
}
bool cmp2(Tiii A, Tiii B)
{
        return get<1>(A)<get<1>(B);
}
bool cmp3(Tiii A, Tiii B)
{
        return get<2>(A)<get<2>(B);
}
vector<pii>ans;

class solution{
public:

        int n,m,i,j,a,b,c;
        void solve()
        {
                cin>>n;
                for(i=1;i<=n;i++)
                {
                        cin>>a>>b>>c;
                        Tiii tmp(a,b,c,i);
                        vec.push_back(tmp);
                }

                sort(all(vec),cmp1);
                int down;
                for(i=0;i<n;i++)
                {
                        down=i;
                        while(down+1<n && get<0>(vec[i]) == get<0>(vec[down+1]) )down++;
                        sort(vec.begin()+i, vec.begin()+down+1,cmp2);

                        int  jdown;
                        for(j=i;j<=down;j++)
                        {
                                jdown=j;
                                while(jdown+1<=down && get<1>(vec[j]) == get<1>(vec[jdown+1]) ) jdown++;
                                sort(vec.begin()+j,vec.begin()+jdown+1, cmp3  );

                                for(int k=j+1;k<=jdown;k+=2)
                                {
                                        ans.push_back({ get<3>(vec[k-1]), get<3>(vec[k]) });
                                        flag[k-1]=1;
                                        flag[k]=1;
                                }

                                j=jdown;
                        }
                        int cnt=0,a;
                        for(int k=i;k<=down;k++)
                        {
                                if(flag[k]==0)
                                {
                                        cnt++;
                                        if(cnt==1)
                                                a=k;
                                        else
                                        {
                                                cnt=0;
                                                ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                                flag[a]=1;
                                                flag[k]=1;
                                        }
                                }
                        }

                        i=down;
                }
                int cnt=0;
                for(int k=0;k<n;k++)
                {
                        if(flag[k]==0)
                        {
                                cnt++;
                                if(cnt==1)
                                        a=k;
                                else
                                {
                                        cnt=0;
                                        ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                        flag[a]=1;
                                        flag[k]=1;
                                }
                        }
                }
                for(auto x: ans)
                {
                        cout<<x.first<<' '<<x.second<<endl;
                }

        }
        ///---->make all int ll must<----
};

int main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        solution ob;
        ob.solve();
	return 0;
}


#include <bits/stdc++.h>
using namespace std;

#define pb(x) push_back(x)
#define memset0(x) memset((x), 0, sizeof(x))
#define memsetM1(x) memset((x), -1, sizeof(x))

#define F first
#define S second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef tuple<int, int, int, int> Tiii;
#define UNIQUE(v) (v).erase(unique((v).begin(), (v).end()), (v).end());
#define all(x) (x).begin(), (x).end()
#define precise(a) fixed<<setprecision(a)
#define isvowel(c) c=='a' || c=='e' || c=='i' || c=='o' || c=='u'

#define SQ(x) ((x)*(x))
#define issq(x) (((ll)(sqrt((x))))*((ll)(sqrt((x))))==(x))
#define mem(a,x) memset(a,x,sizeof(a))
#define inf 1e18
#define lg(r,n) (int)(log2(n)/log2(r))
#define fast ios_base::sync_with_stdio(false)
#define one(x) __builtin_popcount(x)

//#define iceil(n, x) (((n) + (x) - 1) / (x))
//return the minimum number >=n , which is divisible by x
//int next_popcount(int n){int c = (n & -n);int r = n+c;int p = r^n;int x = (p>>2)/c;return x  r;}
// Let, n e k ti onBit ache, this func return a serial combination of k onBit.

//struct BIT{
//    vector<ll> Tree;ll Size=0;void init(int n){Tree.assign(n+1,0);Size=n;}
//    void update(int indx,ll val){while(indx<=n){Tree[indx] += val;indx += (indx)&(-indx);}}
//    ll query(int indx){ll sum=0;while(indx>0)
//        {sum += Tree[indx];sum %= mod;indx -= (indx)&(-indx);}return sum;}
//    ll query(int l, int r){return (query(r) - query(l-1)+mod)%mod;}
//};
//ll bigmod(ll a, ll b){if(b==0) return 1%mod;ll x = bigmod(a,b/2);
//    x = (x*x)%mod;if(b%2==1) x = (x*a)%mod;return x;}
//ll egcd(ll a, ll b, ll &x, ll &y){if(a==0){x=0;y=1;return b;}
//    ll x1,y1;ll d =egcd(b%a, a,x1,y1);x = y1-(b/a)*x1;y=x1;return d;}

//have to check;
#define lcase(a) (char)tolower(a)
#define ucase(a) (char)toupper(a)
#define section(a,b,w) set_intersection(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define union(a,b,w) set_union(a.begin(), a.end(),b.begin(), b.end(),back_inserter(w));
#define copy(a,w) copy(a.begin(), a.end(),back_inserter(w));
//

constexpr double PI = (double)acos(-1);
constexpr double EPS = (double)1e-10;
constexpr int dx[] = {0,1,0,-1};
constexpr int dy[] = {1,0,-1,0};
constexpr int dxx[] = {-1, 0, 0, 1, 0, -1, -1, 1, 1};
constexpr int dyy[] = {0, -1, 1, 0, 0, 1, -1, 1, -1};
inline int toInt(string s) { int v; istringstream sin(s); sin >> v; return v; }

#define bit(n) (1LL << (n))
#define bitset(a, b) (a) |= (1 << (b))
#define bitunset(a, b) (a) &= ~(1 << (b))
#define bitcheck(a, b) ((((a) >> (b)) & 1) == 1)
#define bitcount(a) __builtin_popcountll((a))
inline void bin(ll n){if (n > 1)bin(n>>1); cout<<(n&1);}
inline int binlen(ll x) { if(x==0) return 1; return floor(log2(double(x)))+1;}
#define FRO freopen("in.txt","r",stdin);
#define oFRO freopen("out.txt","w",stdout);
#define DEBUG(args...) do {cerr<<#args<<' '; print(args);} while(0);cerr<<endl;
template<typename T> void print(const T& v){cerr<<v<<' ';}
template<typename T1,typename... T2>
void print(const T1& first,const T2&... rest){print(first);print(rest...);}
template<class T>constexpr const T&_min(const T&x,const T&y){return x<y?x:y;}template<class T>constexpr const T&_max(const T&x,const T&y){return x<y?y:x;}
template<class T,class...Ts>constexpr const T&_min(const T&x,const Ts&...xs){return _min(x,_min(xs...));}
template<class T,class...Ts>constexpr const T&_max(const T&x,const Ts&...xs){return _max(x,_max(xs...));}
template<class T,class...Ts>void MIN(T&x,const Ts&...xs){x=_min(x,xs...);}template<class T,class...Ts>void MAX(T&x,const Ts&...xs){x=_max(x,xs...);}
#define min(...) _min(__VA_ARGS__)
#define max(...) _max(__VA_ARGS__)

//bool chkbnd(int i,int j) {return 0<=i && i<n && 0<=j && j<m;}

ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
// ==============================================================================================

vector<tuple<int,int,int,int>>vec;
bool flag[2001];

bool cmp1(Tiii A, Tiii B)
{
        return get<0>(A)<get<0>(B);
}
bool cmp2(Tiii A, Tiii B)
{
        return get<1>(A)<get<1>(B);
}
bool cmp3(Tiii A, Tiii B)
{
        return get<2>(A)<get<2>(B);
}
vector<pii>ans;

class solution{
public:

        int n,m,i,j,a,b,c;
        void solve()
        {
                cin>>n;
                for(i=1;i<=n;i++)
                {
                        cin>>a>>b>>c;
                        Tiii tmp(a,b,c,i);
                        vec.push_back(tmp);
                }

                sort(all(vec),cmp1);
                int down;
                for(i=0;i<n;i++)
                {
                        down=i;
                        while(down+1<n && get<0>(vec[i]) == get<0>(vec[down+1]) )down++;
                        if(down+1==n)
                                sort(vec.begin()+i, vec.end(),cmp2);
                        else
                                sort(vec.begin()+i, vec.begin()+down+1,cmp2);

                        int  jdown;
                        for(j=i;j<=down;j++)
                        {
                                jdown=j;
                                while(jdown+1<=down && get<1>(vec[j]) == get<1>(vec[jdown+1]) ) jdown++;
                                if(jdown+1==n)
                                        sort(vec.begin()+j,vec.end(), cmp3  );
                                else
                                        sort(vec.begin()+j,vec.begin()+jdown+1, cmp3  );

                                for(int k=j+1;k<=jdown;k+=2)
                                {
                                        ans.push_back({ get<3>(vec[k-1]), get<3>(vec[k]) });
                                        flag[k-1]=1;
                                        flag[k]=1;
                                }

                                j=jdown;
                        }
                        int cnt=0,a;
                        for(int k=i;k<=down;k++)
                        {
                                if(flag[k]==0)
                                {
                                        cnt++;
                                        if(cnt==1)
                                                a=k;
                                        else
                                        {
                                                cnt=0;
                                                ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                                flag[a]=1;
                                                flag[k]=1;
                                        }
                                }
                        }

                        i=down;
                }
                int cnt=0;
                for(int k=0;k<n;k++)
                {
                        if(flag[k]==0)
                        {
                                cnt++;
                                if(cnt==1)
                                        a=k;
                                else
                                {
                                        cnt=0;
                                        ans.push_back({ get<3>(vec[a]), get<3>(vec[k]) });
                                        flag[a]=1;
                                        flag[k]=1;
                                }
                        }
                }
                for(auto x: ans)
                {
                        cout<<x.first<<' '<<x.second<<endl;
                }

        }
        ///---->make all int ll must<----
};

int main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        solution ob;
        ob.solve();
	return 0;
}


#pragma GCC optimize("Ofast")

#include <bits/stdc++.h>
using namespace std;

#define endl '\n'

using ii = pair<int, int>;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using vec = vector<int>;

#define for0(i, n) for(int i = 0; i < n; ++i)
#define for1(i, n) for(int i = 1; i <= n; ++i)
#define fs first
#define sc second
#define sz(a) (int)a.size()
#define all(a) a.begin(), a.end()
#define pb push_back
#define CASE int _T; cin >> _T; for1(tt, _T)

namespace royyaha {
	// input
	template<typename T1, typename T2>													// vector
	void rvec(const T1 &n, vector<T2> &v) {
		v.resize(n); for0(i, n) cin >> v[i];
	}

	//output
	template<typename T> void pr(const T& x) { cout << x; }
	template<typename T1, typename T2>
	void pr(const pair<T1, T2>& x) {
		cout << x.first << " " << x.second;
    }
    template<typename T, typename... Args>
	void pr(const T& x, const Args&... rest) {
        pr(x); pr(rest...);
    }

	template<typename T> void ou(const T& x) { pr(x); cout << endl; }
	template<typename T, typename... Args>
	void ou(const T& x, const Args&... rest) { pr(x, ' '); ou(rest...); }
	template<typename T>
	void ou(const vector<T> &v) {
		for(auto it = v.begin(); it != v.end(); ++it) {									// vector
			if(it + 1 == v.end()) ou(*it);
			else pr(*it, ' ');
		}
	}

}
using namespace royyaha;

#ifdef rrr
#define wer(...) if(1) {																\
	cerr << __PRETTY_FUNCTION__ << " - " << __LINE__ << "(" << #__VA_ARGS__ << ") = ";	\
	ou(__VA_ARGS__);																	\
}
#define IOS
#else
#define wer(...)
#define IOS cin.tie(NULL); ios_base::sync_with_stdio(NULL);
#endif

vector<vec> p;

signed main() {
	int n;
	cin >> n;
	for0(i, n) {
		for0(j, 3) {
			cin >> p[i][j];
		}
	}

	auto solve = [&](auto& Self, vector<int> ids, int k) {
		if(k == 3) return ids[0];
		map<int, vec> mp;
		for(int i : ids) {
			mp[p[i][k]].pb(i);
		}
		vec ans;
		for(auto& t : mp) {
			int cur = Self(Self, t.sc, k + 1);
			if(cur != -1) ans.pb(cur);
		}
		for0(i, sz(ans) / 2) {
			cout << ans[i * 2] + 1 << " " << ans[i * 2 + 1] << endl;
		}
		if(sz(ans) % 2 == 1) return ans.back();
		else return -1;

	};

	vec t(n);
	iota(all(t), 0);
	solve(solve, t, 0);

}