#include <bits/stdc++.h>
using namespace std;
ifstream in("hotel.in");
ofstream out("hotel.out");
int const NMAX = 1e5;
int const SEGMAX = 1 + 3 * NMAX;
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[SEGMAX]; //I added this because I was having memory issues
//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);
if(left < right) {
seg[2 * node].lazySet0 = true;
seg[2 * node].lazySet1 = false;
seg[2 * node + 1].lazySet0 = true;
seg[2 * node + 1].lazySet1 = false;
}
} else if (seg[node].lazySet1 == true) {
seg[node].initFull(node, left, right);
if(left < right) {
seg[2 * node].lazySet0 = false;
seg[2 * node].lazySet1 = true;
seg[2 * node + 1].lazySet0 = false;
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
//EDIT by Aarush: when debugging I found that the children weren't always clean
//so I cleaned them at the beginning
void computeNode(int node, int left, int right) {
int mid = (left + right) / 2;
cleanNode(node, left, right);
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, int 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].lazySet0 = true;
} else {
seg[node].lazySet1 = true;
}
cleanNode(node, left, right);
} 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;
}