#include <iostream>
#include <fstream>
using namespace std;
ifstream in ("hotel.in");
ofstream out ("hotel.out");
int const nmax = 100000;
struct Node {
//campuri
int sol; //nr maxim de camere libere consecutive
int dr; //nr maxim de camere libere consecutive incepand din extrema dreapta
int st; //nr maxim de camere libere consecutive incepand din extrema stanga
//campuri de update lazy
bool empty;
//daca empty == true, toate camere sunt libere, empty == false nu inseamna nimic
bool full;
//daca full == true, toate camere sunt pline, empty == false nu inseamna nimic
void initempty(int node, int from, int to) {
sol = to - from + 1;
dr = sol;
st = sol;
empty = 1;
full = 0;
}
void initfull(int node, int from, int to) {
sol = 0;
dr = 0;
st = 0;
empty = 0;
full = 1;
}
};
Node arb[5 + nmax * 4];
//nodul e clean
//nodul nu e frunza
//fii nodului sunt clean (astea sunt presupunrile tale aici)
//DECI ai grija sa cureti si fiii in cleannode!
void computenode(int node , int from, int to) {
Node &a = arb[2 * node];
Node &b = arb[2 * node + 1];
arb[node].sol = max(max(a.sol , b.sol), a.dr + b.st);
if(b.empty)
arb[node].dr = a.dr + b.dr;
else
arb[node].dr = b.dr;
if(a.empty)
arb[node].st = a.st + b.st;
else
arb[node].st = a.st;
arb[node].empty = a.empty & b.empty;
arb[node].full = a.full & b.full;
}
void buildtree(int node, int from, int to) {
if(from == to) {
arb[node].initempty(node, from, to);
} else {
int mid = (from + to) / 2;
buildtree(node * 2, from, mid);
buildtree(node * 2 + 1, mid + 1 , to);
computenode(node, from, to);
}
}
//tu consideri ca node e clean si de fapt ii cureti fiii
void cleannode(int node , int from , int to) {
if(from < to) {
int mid = (from + to) / 2;
if(arb[node].empty == 1) {
arb[node * 2].initempty(node * 2 , from , mid);
arb[node * 2 + 1].initempty(node * 2 + 1 , mid + 1, to);
} else if(arb[node].full == 1){
arb[node * 2].initfull(node * 2 , from , mid);
arb[node * 2 + 1].initfull(node * 2 + 1 , mid + 1, to);
}
}
}
void update(int node ,int from , int to , int x , int y , bool fill) {
if(from == x && to == y){
if(fill == 1)
arb[node].initfull(node , from , to); //initializare harnica
else
arb[node].initempty(node , from , to);
//cleannode(node, from, to);
//tu cand mergi de sus in jos si vezi primul nod empty sau full, tu trebuie sa stii ca ai sub el doi fii curati
} else {
cleannode(node, from, to);
int mid = (from + to) / 2;
if(x <= mid)
update(node * 2, from, mid, x, min(mid, y), fill);
if(mid + 1 <= y)
update(node * 2 + 1 , mid + 1 , to , max(mid + 1 , x) , y , fill);
computenode(node , from , to);
}
}
void printtree(int node , int from , int to){
cout<<node<<" "<<from<<" "<<to<<" "<<arb[node].st<<" "<<arb[node].dr<<" "<<arb[node].full<<" "<<arb[node].empty<<'\n';
if(from < to){
int mid = (from + to) / 2;
printtree(node * 2 , from ,mid);
printtree(node * 2 + 1 , mid + 1 , to);
}
}
int main(){
int n, m;
in >> n >> m;
buildtree(1 , 1 , n);
//printtree(1 , 1 , n);
//cout<<'\n';
for(int i = 1 ; i <= m ;i++){
int p;
in>>p;
if(p == 1){
int x , y;
in>>x>>y;
y = x + y - 1;
update(1 , 1 , n , x , y , 1);
} else if(p == 2){
int x , y;
in>>x>>y;
y = x + y - 1;
update(1 , 1 , n , x , y , 0);
} else{
out<<arb[1].sol<<'\n';
}
//printtree(1 , 1 , n);
//cout<<'\n';
}
}