#include <bits/stdc++.h>
using namespace std;
ifstream in("hotel.in");
ofstream out("hotel.out");
int const NMAX = 1e5;
struct NodeInfo {
//lazy flag fields
bool lazySet0;
bool lazySet1;
//actual information fields
int maxConsZeros;
int maxZerosAtStart;
int maxZerosAtEnd;
void initEmpty(int node, int left, int right) {
lazySet0 = false;
lazySet1 = false;
maxConsZeros = right - left + 1;
maxZerosAtStart = right - left + 1;
maxZerosAtEnd = right - left + 1;
}
void initFull(int node, int left, int right) {
lazySet0 = false;
lazySet1 = false;
maxConsZeros = 0;
maxZerosAtStart = 0;
maxZerosAtEnd = 0;
}
};
NodeInfo seg[1 + 8 * NMAX];
//COMMENT: make sure at one point you clean the mess (or push it outside the tree)
//We are assuming that through our model a node cannot be at the same time lazily set to 0 and lazily to to 1.
void cleanNode(int node, int left, int right) {
if(seg[node].lazySet0 == true) {
seg[node].initEmpty(node, left, right);
seg[2*node].lazySet0 = true;
seg[2*node+1].lazySet0 = true;
} else if(seg[node].lazySet1 == true) {
seg[node].initFull(node, left, right);
seg[2*node].lazySet1 = true;
seg[2*node+1].lazySet1 = true;
}
}
//Here is going back up the tree after an update => we are working with real info
//The node is clean and his children are clean
void computeNode(int node, int left, int right) {
int mid = (left + right)/2;
cleanNode(2*node, left, mid);
cleanNode(2*node + 1, mid+1, right);
NodeInfo &son1 = seg[2*node];
NodeInfo &son2 = seg[2*node+1];
int size1 = mid - left + 1;
int size2 = right - (mid+1) + 1;
int maxZeros = max(son1.maxConsZeros, son2.maxConsZeros);
seg[node].maxConsZeros = max(maxZeros, son1.maxZerosAtEnd + son2.maxZerosAtStart);
seg[node].maxZerosAtStart = son1.maxZerosAtStart;
if(son1.maxZerosAtStart == size1) {
seg[node].maxZerosAtStart += son2.maxZerosAtStart;
}
seg[node].maxZerosAtEnd = son2.maxZerosAtEnd;
if(son2.maxZerosAtEnd == size2) {
seg[node].maxZerosAtEnd += son1.maxZerosAtEnd;
}
//cout << "(" << node << " " << seg[node].maxConsZeros << ")" << "\n";
}
//PREVIOUSLY, update(int node, int left, int right, int pos, int value)
void update(int node, int left, int right, int from, int to, bool val) {
//cout << "(" << node << ", " << left << ", " << right << ") " << "[" << from << ", " << to << "] " << val << "\n";
cleanNode(node, left, right);
int mid = (left + right)/2;
if(left == from && right == to) {
if(val == 0) {
seg[node].initEmpty(node, left, right);
seg[2*node].lazySet0 = seg[2*node+1].lazySet0 = true;
} else {
seg[node].initFull(node, left, right);
seg[2*node].lazySet1 = seg[2*node+1].lazySet1 = true;
}
} else {
if(from <= mid) {
update(2*node, left, mid, from, min(mid, to), val);
}
if(mid+1 <= to) {
update(2*node + 1, mid+1, right, max(mid+1, from), to, val);
}
computeNode(node, left, right);
}
}
//No point in being lazy
void build(int node, int left, int right) {
seg[node].initEmpty(node, left, right);
if(left != right) {
int mid = (left + right)/2;
build(2*node, left, mid);
build(2*node + 1, mid+1, right);
}
}
//in this problem updating from zero/initial state does not work, because initial zero/state has some specific modeling needed.
int main() {
int n, p;
in >> n >> p;
build(1, 1, n);
for(int i = 1; i <= p; i++) {
int c, k1, k2;
in >> c;
if(c == 1) {
in >> k1 >> k2;
update(1, 1, n, k1, k1+k2-1, 1);
} else if(c == 2) {
in >> k1 >> k2;
update(1, 1, n, k1, k1+k2-1, 0);
} else {
out << seg[1].maxConsZeros << "\n";
}
}
return 0;
}