/*
看完题目:靠,这什么玩意儿?能做?
看完数据范围:靠,似乎还可作!
*/
注意到hi<=10,xi<=10,保证任何时刻高度两两不同,于是我们踏上了暴力之路。。。。
我们需要维护的是数组的最长上升序列。
dp[i]表示开头为i的最长上升序列
操作1 对于新增的i,一定是在其后的高度大于hi的树才能转移,而受到i影响的一定是i之前的高度小于hi的。维护一个线段树,以x为下标,dp为权值,支持区间求最大值。我们将高度大于10的全部树的dp全部放进去,小于10的由于总数不会超过10个,直接暴力即可。
操作2 找到第x个树之后(我用的是树状数组),乱搞啦!在维护一颗线段树,以高度为下标,dp为权值,维护除前10棵树的最大值。利用者棵线段树+暴力重新计算前10棵树的dp
/*不要问我复杂度,我自己也不会算了。。。。*/
#include <list>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
int maxx[400005],dp[100005],tr[100005];
int maxx2[800020],ht[100005];
int n,m,M,tim,ans,x,y,xx,typ,i,j;
list <int> c;
list <int>::iterator I,J;
void add(int p,int l,int r,int x){
if (l==r && l==x) {maxx[p]=dp[x]; return;}
int mid=(l+r)>>1;
if (x<=mid) add(p*2+1,l,mid,x);
else add(p*2+2,mid+1,r,x);
maxx[p]=max(maxx[p*2+1],maxx[p*2+2]);
}
int query(int p,int l,int r,int x){
if (l>=x) return maxx[p];
int mid=(l+r)>>1;
if (mid<x) return query(p*2+2,mid+1,r,x);
else return max( query(p*2+1,l,mid,x), query(p*2+2,mid+1,r,x) );
}
int ADD(int x,int flag){
for (int i=x;i<=n;i+=lowbit(i))
tr[i]+=flag;
}
int GET(int x){
int ret=0;
for (int i=x;i>0;i-=lowbit(i))
ret+=tr[i];
return ret;
}
int calc(int x){
int ret=0;
for (int l=1,r=n,mid=(l+r)>>1;l<=r;mid=(l+r)>>1){
if (x<=GET(mid)) ret=mid, r=mid-1;
else l=mid+1;
}
return ret;
}
void add2(int p,int l,int r,int x,bool flag){
if (l==r && l==ht[x]+m){
if (flag) maxx2[p]=dp[x];
else maxx2[p]=0;
return;
}
int mid=(l+r)>>1;
if (ht[x]+m<=mid) add2(p*2+1,l,mid,x,flag);
else add2(p*2+2,mid+1,r,x,flag);
maxx2[p]=max(maxx2[p*2+1],maxx2[p*2+2]);
}
int query2(int p,int l,int r,int x){
if (l>=x) return maxx2[p];
int mid=(l+r)>>1;
if (x>mid) return query2(p*2+2,mid+1,r,x);
else return max( query2(p*2+1,l,mid,x), query2(p*2+2,mid+1,r,x) );
}
int main(){
//freopen("264E.in","r",stdin);
//freopen("264E.out","w",stdout);
scanf("%d%d",&n,&m);
M=m+10;
for (tim=1;tim<=m;tim++){
for (I=c.begin();I!=c.end();I++)
if (ht[*I]+tim>10)
{add(0,1,n+1,*I); c.erase(I); break;}
scanf("%d",&typ);
if (typ==1){
scanf("%d",&x);
scanf("%d",&ht[x]);
ht[x]-=tim;
ADD(x, 1);
c.push_back(x);
c.sort();
dp[x]=query(0,1,n+1,x+1)+1;
I=find(c.begin(),c.end(),x);
for (I++;I!=c.end();I++)
if (ht[*I]>ht[x])
dp[x]=max(dp[x],dp[*I]+1);
I=find(c.begin(),c.end(),x);
if (I!=c.begin())
for (I--;;I--){
for (J=I,J++;J!=c.end();J++)
if (ht[*I]<ht[*J])
dp[*I]=max(dp[*I],dp[*J]+1);
if (GET(*I)>10) add2(0,1,M,*I,1);
if (I==c.begin()) break;
}
if (GET(n)>10){
if (GET(x)<=10) add2(0,1,M,calc(11),1);
else add2(0,1,M,x,1);
}
} else
if (typ==2){
scanf("%d",&y);
x=calc(y); ADD(x, -1);
if (GET(n)>=10)
add2(0,1,M,calc(10),0);
I=find(c.begin(),c.end(),x);
dp[x]=0;
if (I==c.end()) add(0,1,n+1,x);
else c.erase(I);
//calc it again
for (i=1;i<y;i++){
x=calc(i);
dp[x]=query2(0,1,M,ht[x]+m)+1;
}
for (i=y-1;i>0;i--){
x=calc(i);
for (j=i+1;j<=10;j++){
xx=calc(j);
if (ht[x]<ht[xx])
dp[x]=max(dp[x],dp[xx]+1);
}
if (ht[x]+tim>10)
add(0,1,n+1,x);
}
}
ans=query(0,1,n+1,1);
for (I=c.begin();I!=c.end();I++)
if (ans<dp[*I]) ans=dp[*I];
printf("%d\n",ans);
//for (i=1;i<=tim;i++)
// printf("%d ",ht[ calc(i) ]+tim);
//printf("\n");
//printf("%d\n",calc(3));
}
return 0;
}