#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=8010;
struct Node
{
int l,r;
int color;
}tr[MAXN*3];
int color[MAXN];
int temp;
void build(int i,int l,int r)
{
tr[i].l=l;
tr[i].r=r;
//-1表示没有颜色
tr[i].color=-1;
if(l+1==r)
return;
int mid=((l+r)>>1);
build(i<<1,l,mid);
build((i<<1)|1,mid,r);
}
void insert(int i,int l,int r,int c)
{
if(l==r)
return;
if(tr[i].color==c)
return;
if(l<=tr[i].l&&r>=tr[i].r)
{
tr[i].color=c;
return;
}
//存在颜色,往下更新
if(tr[i].color>=0)
{
tr[i<<1].color=tr[i].color;
tr[(i<<1)|1].color=tr[i].color;
//表示有多种颜色
tr[i].color=-2;
}
int mid=((tr[i].l+tr[i].r)>>1);
if(r<=mid)
insert(i<<1,l,r,c);
else if(l>=mid)
insert((i<<1)|1,l,r,c);
else
{
insert(i<<1,l,mid,c);
insert((i<<1)|1,mid,r,c);
}
tr[i].color=-2;
}
//统计各颜色的段数
void query(int i)
{
if(tr[i].color==-1)
{
temp=-1;
return;
}
if(tr[i].color!=-2)
{
//temp存的是前一段的颜色
if(tr[i].color!=temp)
{
color[tr[i].color]++;
temp=tr[i].color;
}
return;
}
if(tr[i].l+1!=tr[i].r)
{
query(i<<1);
query((i<<1)|1);
}
}
int main()
{
int n,a,b,c;
int Max;
while(scanf("%d",&n)!=EOF)
{
build(1,0,8000);
Max=0;
while(n--)
{
scanf("%d%d%d",&a,&b,&c);
insert(1,a,b,c);
if(c>Max)
Max=c;
}
temp=-1;
memset(color,0,sizeof(color));
query(1);
for(int i=0;i<=Max;i++)
if(color[i])
printf("%d %d\n",i,color[i]);
printf("\n");
}
return 0;
}