#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

#define NMAX 1000010

using namespace std;

int read(int x, int *tree){
	int sum = 0;

	for(; x >= 0; x -= (x&-x)){
		sum += tree[x];
		if(!x) break;
	}

	return sum;
}

int upd(int x, int val, int *tree, int n){
	for(; x < n; x+= (x&-x)){
		tree[x] += val;
		if(!x) break;
	}

	return x;
}

int *trees[NMAX];
int n[NMAX];
int *times[NMAX];

struct query{
	int typ,time,x;
} qs[NMAX];

int vals[NMAX];
int N,Q;

int main(){
	int i;

	scanf("%d",&Q);

	for(i =0 ; i < Q; ++i){
		scanf("%d%d%d",&qs[i].typ,&qs[i].time,&qs[i].x);
		vals[i] = qs[i].x;
	}

	sort(vals,vals+Q);
	N = unique(vals,vals+Q)-vals;

	for(i = 0; i < Q; ++i){
		qs[i].x = lower_bound(vals,vals+Q,qs[i].x)-vals;
		++n[qs[i].x];
	}

	for(i =0 ; i < N; ++i){
		times[i] = new int[n[i]]();
		n[i] = 0;
	}

	for(i = 0; i < Q; ++i){
		times[qs[i].x][n[qs[i].x]++] = qs[i].time;
	}

	for(i = 0; i < N; ++i){
		sort(times[i],times[i]+n[i]);
		n[i] = unique(times[i],times[i]+n[i])-times[i];
		trees[i] = new int[n[i]]();
	}

	for(i = 0; i < Q; ++i){
		qs[i].time = lower_bound(times[qs[i].x],times[qs[i].x]+n[qs[i].x],qs[i].time)-times[qs[i].x];
		if(qs[i].typ < 3){
			upd(qs[i].time,(qs[i].typ == 1 ? 1 : -1),trees[qs[i].x],n[qs[i].x]);
			continue;
		}
		printf("%d\n",read(qs[i].time,trees[qs[i].x]));
	}

	return 0;
}


#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

#define NMAX 1000010

using namespace std;

int read(int x, int *tree){
	int sum = 0;

	for(; x >= 0; x -= (x&-x)){
		sum += tree[x];
		if(!x) break;
	}

	return sum;
}

int upd(int x, int val, int *tree, int n){
	for(; x < n; x+= (x&-x)){
		tree[x] += val;
		if(!x) break;
	}

	return x;
}

int *trees[NMAX];
int n[NMAX];
int *times[NMAX];

struct query{
	int typ,time,x;
} qs[NMAX];

int vals[NMAX];
int N,Q;

int main(){
	int i;

	scanf("%d",&Q);

	for(i =0 ; i < Q; ++i){
		scanf("%d%d%d",&qs[i].typ,&qs[i].time,&qs[i].x);
		vals[i] = qs[i].x;
	}

	sort(vals,vals+Q);
	N = unique(vals,vals+Q)-vals;

	for(i = 0; i < Q; ++i){
		qs[i].x = lower_bound(vals,vals+Q,qs[i].x)-vals;
		++n[qs[i].x];
	}

	for(i =0 ; i < N; ++i){
		times[i] = new int[n[i]];
		n[i] = 0;
	}

	for(i = 0; i < Q; ++i){
		times[qs[i].x][n[qs[i].x]++] = qs[i].time;
	}

	for(i = 0; i < N; ++i){
		sort(times[i],times[i]+n[i]);
		n[i] = unique(times[i],times[i]+n[i])-times[i];
		trees[i] = new int[n[i]];
	}

	for(i = 0; i < Q; ++i){
		qs[i].time = lower_bound(times[qs[i].x],times[qs[i].x]+n[qs[i].x],qs[i].time)-times[qs[i].x];
		if(qs[i].typ < 3){
			upd(qs[i].time,(qs[i].typ == 1 ? 1 : -1),trees[qs[i].x],n[qs[i].x]);
			continue;
		}
		printf("%d\n",read(qs[i].time,trees[qs[i].x]));
	}

	return 0;
}


#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
#include <cstdio>
#include <cassert>

#define NMAX 100010

using namespace std;

int read(int x, vector<int> &tree){
	int sum = 0;

	if(x >= tree.size()) x = tree.size()-1;
	for(; x >= 0; x -= (x&-x)){
		sum += tree[x];
		if(!x) break;
	}

	return sum;
}

int upd(int x, int val, vector<int> &tree){
	for(; x < tree.size(); x+= (x&-x)){
		tree[x] += val;
		if(!x) break;
	}

	return x;
}

vector<int> trees[NMAX];
vector<int> times[NMAX];

struct query{
	int typ,time,x;
} qs[NMAX];

int vals[NMAX];
int N,Q;

int main(){
	int i;

	scanf("%d",&Q);

	for(i =0 ; i < Q; ++i){
		scanf("%d%d%d",&qs[i].typ,&qs[i].time,&qs[i].x);
		vals[i] = qs[i].x;
	}

	sort(vals,vals+Q);
	N = unique(vals,vals+Q)-vals;

	for(i = 0; i < Q; ++i){
		qs[i].x = lower_bound(vals,vals+Q,qs[i].x)-vals;
		times[qs[i].x].push_back(qs[i].time);
	}

	for(i = 0; i < N; ++i){
		sort(times[i].begin(),times[i].end());
		trees[i].resize(times[i].size());
	}

	int x;
	for(i = 0; i < Q; ++i){
		x = qs[i].x;
		qs[i].time = lower_bound(times[x].begin(),times[x].end(),qs[i].time)-times[x].begin();

		if(qs[i].typ < 3){
			upd(qs[i].time,(qs[i].typ == 1 ? 1 : -1),trees[x]);
			continue;
		}

		printf("%d\n",read(qs[i].time,trees[x]));
	}

	return 0;
}


#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
#include <cstdio>
#include <cassert>

#define NMAX 100010

using namespace std;

int read(int x, vector<int> &tree){
	int sum = 0;

	assert(x < tree.size());
	for(; x >= 0; x -= (x&-x)){
		sum += tree[x];
		if(!x) break;
	}

	return sum;
}

int upd(int x, int val, vector<int> &tree){
	for(; x < tree.size(); x+= (x&-x)){
		tree[x] += val;
		if(!x) break;
	}

	return x;
}

vector<int> trees[NMAX];
int n[NMAX];
vector<int> times[NMAX];

struct query{
	int typ,time,x;
} qs[NMAX];

int vals[NMAX];
int N,Q;

int main(){
	int i;

	scanf("%d",&Q);

	for(i =0 ; i < Q; ++i){
		scanf("%d%d%d",&qs[i].typ,&qs[i].time,&qs[i].x);
		vals[i] = qs[i].x;
	}

	sort(vals,vals+Q);
	N = unique(vals,vals+Q)-vals;

	for(i = 0; i < Q; ++i){
		qs[i].x = lower_bound(vals,vals+Q,qs[i].x)-vals;
		times[qs[i].x].push_back(qs[i].time);
	}

	for(i = 0; i < N; ++i){
		sort(times[i].begin(),times[i].end());
		n[i] = unique(times[i].begin(),times[i].end())-times[i].begin();
		trees[i].resize(n[i]);
	}

	int x;
	for(i = 0; i < Q; ++i){
		x = qs[i].x;
		qs[i].time = lower_bound(times[x].begin(),times[x].begin()+n[x],qs[i].time)-times[x].begin();
		if(qs[i].typ < 3){
			upd(qs[i].time,(qs[i].typ == 1 ? 1 : -1),trees[x]);
			continue;
		}
		printf("%d\n",read(qs[i].time,trees[x]));
	}

	return 0;
}


#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
#include <cstdio>
#include <cassert>

#define NMAX 100010

using namespace std;

int read(int x, vector<int> &tree){
	int sum = 0;

	assert(x < tree.size());
	for(; x >= 0; x -= (x&-x)){
		sum += tree[x];
		if(!x) break;
	}

	return sum;
}

int upd(int x, int val, vector<int> &tree){
	for(; x < tree.size(); x+= (x&-x)){
		tree[x] += val;
		if(!x) break;
	}

	return x;
}

vector<int> trees[NMAX];
vector<int> times[NMAX];

struct query{
	int typ,time,x;
} qs[NMAX];

int vals[NMAX];
int N,Q;

int main(){
	int i;

	scanf("%d",&Q);

	for(i =0 ; i < Q; ++i){
		scanf("%d%d%d",&qs[i].typ,&qs[i].time,&qs[i].x);
		vals[i] = qs[i].x;
	}

	sort(vals,vals+Q);
	N = unique(vals,vals+Q)-vals;

	for(i = 0; i < Q; ++i){
		qs[i].x = lower_bound(vals,vals+Q,qs[i].x)-vals;
		times[qs[i].x].push_back(qs[i].time);
	}

	for(i = 0; i < N; ++i){
		sort(times[i].begin(),times[i].end());
		trees[i].resize(times[i].size());
	}

	int x;
	for(i = 0; i < Q; ++i){
		x = qs[i].x;
		qs[i].time = lower_bound(times[x].begin(),times[x].end(),qs[i].time)-times[x].begin();

		if(qs[i].typ < 3){
			upd(qs[i].time,(qs[i].typ == 1 ? 1 : -1),trees[x]);
			continue;
		}

		printf("%d\n",read(qs[i].time,trees[x]));
	}

	return 0;
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <cmath>
using namespace std;

int bucket;
vector<pair<int, pair<int, int>>> q;

int main(void)
{
	int n;
	scanf("%d", &n);
	q.clear(); q.resize(n);

	vector<pair<int, int>> compressT;

	for(int i = 0;i < n;i++)
	{
		scanf("%d%d%d", &q[i].first, &q[i].second.first, &q[i].second.second);
		compressT.push_back({q[i].second.first, i});
	}

	sort(compressT.begin(), compressT.end());

	int v = 1;
	for(int i = 0;i < n;i++) q[compressT[i].second].second.first = v++;

	bucket = int(sqrt(double(v)))+1;

	vector<map<int, int>> state(bucket+1);
	vector<int> updadd(v, -1), updsub(v, -1);

	for(auto it: q)
	{
		if(it.first == 1)
		{
			updadd[it.second.first] = it.second.second;
			for(int i = 0;i < int(state.size());i++)
			{
				if(i*bucket >= it.second.first)
				{
					if(state[i].find(it.second.second) != state[i].end()) state[i][it.second.second]++;
					else state[i][it.second.second] = 1;
				}
			}
		}
		else if(it.first == 2)
		{
			updsub[it.second.first] = it.second.second;
			for(int i = 0;i < int(state.size());i++)
			{
				if(i*bucket >= it.second.first)
				{
					state[i][it.second.second]--;
				}
			}
		}
		else
		{
			int res = 0, i;
			for(i = int(state.size())-1;i >= 0;i--)
			{
				if(i*bucket <= it.second.first)
				{
					if(state[i].find(it.second.second) != state[i].end()) res += state[i][it.second.second];
					break;
				}
			}
			for(int x = i*bucket+1;x <= it.second.first;x++)
			{
				res += (updadd[x]==it.second.second);
				res -= (updsub[x]==it.second.second);
			}
			printf("%d\n", res);
		}
	}
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;

int bucket;
vector<pair<int, pair<int, int>>> q;

int main(void)
{
	int n;
	scanf("%d", &n);
	q.clear(); q.resize(n);

	vector<pair<int, int>> compressT;
	set<int> compressorA;
	map<int, int> compressA;

	for(int i = 0;i < n;i++)
	{
		scanf("%d%d%d", &q[i].first, &q[i].second.first, &q[i].second.second);
		compressT.push_back({q[i].second.first, i});
		compressorA.insert(q[i].second.second);
	}

	sort(compressT.begin(), compressT.end());

	int ah = 1;
	for(auto it: compressorA) compressA[it] = ah++;
	int v = 1;
	for(int i = 0;i < n;i++)
	{
		q[compressT[i].second].second.first = v++;
		//cout << q[i].second.second << " ";
		q[i].second.second = compressA[q[i].second.second];
		//cout << q[i].second.second << "\n";
	}

	bucket = int(sqrt(double(v)))+1;

	vector<vector<int>> state(bucket+1, vector<int>(ah+10));
	vector<int> updadd(v+1, -1), updsub(v+1, -1);

	for(auto it: q)
	{
		if(it.first == 1)
		{
			updadd[it.second.first] = it.second.second;
			for(int i = int(it.second.first/bucket)+1;i < int(state.size());i++)
			{
				state[i][it.second.second]++;
			}
		}
		else if(it.first == 2)
		{
			updsub[it.second.first] = it.second.second;
			for(int i = int(it.second.first/bucket)+1;i < int(state.size());i++)
			{
				state[i][it.second.second]--;
			}
		}
		else
		{
			int res = 0;

			res += state[int(it.second.first/bucket)][it.second.second];
			for(int x = int(it.second.first/bucket)*bucket+1;x <= it.second.first;x++)
			{
				res += (updadd[x]==it.second.second);
				res -= (updsub[x]==it.second.second);
			}

			printf("%d\n", res);
		}
	}
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;

int bucket;
vector<pair<int, pair<int, int>>> q;

int main(void)
{
	int n;
	scanf("%d", &n);
	q.clear(); q.resize(n);

	vector<pair<int, int>> compressT;
	set<int> compressorA;
	map<int, int> compressA;

	for(int i = 0;i < n;i++)
	{
		scanf("%d%d%d", &q[i].first, &q[i].second.first, &q[i].second.second);
		compressT.push_back({q[i].second.first, i});
		compressorA.insert(q[i].second.second);
	}

	sort(compressT.begin(), compressT.end());

	int ah = 1;
	for(auto it: compressorA) compressA[it] = ah++;
	int v = 1;
	for(int i = 0;i < n;i++)
	{
		q[compressT[i].second].second.first = v++;
		q[i].second.second = compressA[q[i].second.second];
	}

	bucket = int(sqrt(double(v)))+1;

	vector<vector<int>> state(bucket+1, vector<int>(ah+10));
	vector<int> updadd(v, -1), updsub(v, -1);

	for(auto it: q)
	{
		if(it.first == 1)
		{
			updadd[it.second.first] = it.second.second;
			for(int i = int(it.second.first/bucket)+bool(it.second.first%bucket);i < int(state.size());i++)
			{
				state[i][it.second.second]++;
			}
		}
		else if(it.first == 2)
		{
			updsub[it.second.first] = it.second.second;
			for(int i = int(it.second.first/bucket)+bool(it.second.first%bucket);i < int(state.size());i++)
			{
				state[i][it.second.second]--;
			}
		}
		else
		{
			int res = 0;

			res += state[int(it.second.first/bucket)][it.second.second];
			for(int x = (int(it.second.first/bucket)*bucket)+1;x <= it.second.first;x++)
			{
				res += (updadd[x]==it.second.second);
				res -= (updsub[x]==it.second.second);
			}

			printf("%d\n", res);
		}
	}
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;

int bucket;
vector<pair<int, pair<int, int>>> q;

int main(void)
{
	int n;
	scanf("%d", &n);
	q.clear(); q.resize(n);

	vector<pair<int, int>> compressT;
	set<int> compressorA;
	map<int, int> compressA;

	for(int i = 0;i < n;i++)
	{
		scanf("%d%d%d", &q[i].first, &q[i].second.first, &q[i].second.second);
		compressT.push_back({q[i].second.first, i});
		compressorA.insert(q[i].second.second);
	}

	sort(compressT.begin(), compressT.end());

	int ah = 1;
	for(auto it: compressorA) compressA[it] = ah++;
	int v = 1;
	for(int i = 0;i < n;i++)
	{
		q[compressT[i].second].second.first = v++;
		q[i].second.second = compressA[q[i].second.second];
	}

	bucket = int(sqrt(double(v)))+1;

	vector<vector<int>> state(bucket+1, vector<int>(ah+10));
	vector<int> updadd(v, -1), updsub(v, -1);

	for(auto it: q)
	{
		if(it.first == 1)
		{
			updadd[it.second.first] = it.second.second;
			for(int i = int(it.second.first/bucket)+bool(it.second.first%bucket);i < int(state.size());i++)
			{
				state[i][it.second.second]++;
			}
		}
		else if(it.first == 2)
		{
			updsub[it.second.first] = it.second.second;
			for(int i = int(it.second.first/bucket)+1;i < int(state.size());i++)
			{
				state[i][it.second.second]--;
			}
		}
		else
		{
			int res = 0;

			res += state[int(it.second.first/bucket)][it.second.second];
			for(int x = (int(it.second.first/bucket)*bucket)+bool(it.second.first%bucket);x <= it.second.first;x++)
			{
				res += (updadd[x]==it.second.second);
				res -= (updsub[x]==it.second.second);
			}

			printf("%d\n", res);
		}
	}
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;

int bucket;
vector<pair<int, pair<int, int>>> q;

int main(void)
{
	int n;
	scanf("%d", &n);
	q.clear(); q.resize(n);

	vector<pair<int, int>> compressT;
	set<int> compressorA;
	map<int, int> compressA;

	for(int i = 0;i < n;i++)
	{
		scanf("%d%d%d", &q[i].first, &q[i].second.first, &q[i].second.second);
		compressT.push_back({q[i].second.first, i});
		compressorA.insert(q[i].second.second);
	}

	sort(compressT.begin(), compressT.end());

	int ah = 1;
	for(auto it: compressorA) compressA[it] = ah++;
	int v = 1;
	for(int i = 0;i < n;i++)
	{
		q[compressT[i].second].second.first = v++;
		q[i].second.second = compressA[q[i].second.second];
	}

	bucket = int(sqrt(double(v)))+1;

	vector<vector<int>> state(bucket+1, vector<int>(ah+1));
	vector<int> updadd(v, -1), updsub(v, -1);

	for(auto it: q)
	{
		if(it.first == 1)
		{
			updadd[it.second.first] = it.second.second;
			for(int i = int(it.second.first/bucket)+1;i < int(state.size());i++)
			{
				state[i][it.second.second]++;
			}
		}
		else if(it.first == 2)
		{
			updsub[it.second.first] = it.second.second;
			for(int i = int(it.second.first/bucket)+1;i < int(state.size());i++)
			{
				state[i][it.second.second]--;
			}
		}
		else
		{
			int res = 0;

			res += state[int(it.second.first/bucket)][it.second.second];
			for(int x = int(it.second.first/bucket)*bucket+1;x <= it.second.first;x++)
			{
				res += (updadd[x]==it.second.second);
				res -= (updsub[x]==it.second.second);
			}

			printf("%d\n", res);
		}
	}
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <cmath>
using namespace std;

int bucket;
vector<vector<int>> q;

int main(void)
{
	int n;
	scanf("%d", &n);
	q.clear(); q.resize(n, vector<int>(3));

	set<int> compressT;
	map<int, int> T;

	for(int i = 0;i < n;i++)
	{
		scanf("%d%d%d", &q[i][0], &q[i][1], &q[i][2]);
		compressT.insert(q[i][1]);
	}

	int v = 1;
	for(auto it: compressT) T[it] = v++;
	for(int i = 0;i < n;i++) q[i][1] = T[q[i][1]];

	bucket = int(sqrt(double(v)))+1;

	vector<map<int, int>> state(bucket+1);
	vector<int> updadd(v+1, -1), updsub(v+1, -1);

	for(auto it: q)
	{
		//for(auto gt : it) cout << gt << " ";
		//cout << "\n";

		if(it[0] == 1)
		{
			updadd[it[1]] = it[2];
			for(int i = 0;i < int(state.size());i++)
			{
				if(i*bucket >= it[1])
				{
					if(state[i].find(it[2]) != state[i].end()) state[i][it[2]]++;
					else state[i][it[2]] = 1;
				}
			}
		}
		else if(it[0] == 2)
		{
			updsub[it[1]] = it[2];
			for(int i = 0;i < int(state.size());i++)
			{
				if(i*bucket >= it[1])
				{
					state[i][it[2]]--;
				}
			}
		}
		else
		{
			int res = 0, i;
			for(i = int(state.size())-1;i >= 0;i--)
			{
				if(i*bucket <= it[1])
				{
					if(state[i].find(it[2]) != state[i].end()) res += state[i][it[2]];
					break;
				}
			}
			for(int x = i*bucket+1;x <= it[1];x++)
			{
				res += (updadd[x]==it[2]);
				res -= (updsub[x]==it[2]);
			}
			printf("%d\n", res);
		}
	}
}


#include <iostream>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;

int bucket;
vector<pair<int, pair<int, int>>> q;

int main(void)
{
	int n;
	scanf("%d", &n);
	q.clear(); q.resize(n);

	vector<pair<int, int>> compressT;

	for(int i = 0;i < n;i++)
	{
		scanf("%d%d%d", &q[i].first, &q[i].second.first, &q[i].second.second);
		compressT.push_back({q[i].second.first, i});
	}

	sort(compressT.begin(), compressT.end());

	int v = 1;
	for(int i = 0;i < n;i++) q[compressT[i].second].second.first = v++;

	bucket = int(sqrt(double(v)))+1;

	vector<map<int, int>> state(bucket+1);
	vector<int> updadd(v, -1), updsub(v, -1);

	for(auto it: q)
	{
		if(it.first == 1)
		{
			updadd[it.second.first] = it.second.second;
			for(int i = 0;i < int(state.size());i++)
			{
				if(i*bucket >= it.second.first)
				{
					if(state[i].find(it.second.second) != state[i].end()) state[i][it.second.second]++;
					else state[i][it.second.second] = 1;
				}
			}
		}
		else if(it.first == 2)
		{
			updsub[it.second.first] = it.second.second;
			for(int i = 0;i < int(state.size());i++)
			{
				if(i*bucket >= it.second.first)
				{
					state[i][it.second.second]--;
				}
			}
		}
		else
		{
			int res = 0, i;
			for(i = int(state.size())-1;i >= 0;i--)
			{
				if(i*bucket <= it.second.first)
				{
					if(state[i].find(it.second.second) != state[i].end()) res += state[i][it.second.second];
					break;
				}
			}
			for(int x = i*bucket+1;x <= it.second.first;x++)
			{
				res += (updadd[x]==it.second.second);
				res -= (updsub[x]==it.second.second);
			}
			printf("%d\n", res);
		}
	}
}


#include <bits/stdc++.h>
using namespace std;
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define ll unsigned long long
#define ii pair<int,int>
#define mp make_pair
#define pb push_back
#define INF 1000000000
int n,a[100005],t[100005],x[100005];
vector<int> v;
multiset<int> ft[100005];
void add(int p,int v) {
	for (;p<=n;p+=p&-p) ft[p].insert(v);
}
int query(int p,int v) {
	int r=0;
	for (;p;p-=p&-p) r+=ft[p].count(v);
	return r;
}
int main() {
	scanf("%d",&n);
	for (int i=0;i<n;i++) {
		scanf("%d%d%d",&a[i],&t[i],&x[i]);
		v.pb(x[i]);
	}
	sort(all(v));
	for (int i=0;i<n;i++) {
		t[i]=(lower_bound(all(v),t[i])-v.begin()+1);
	}
	for (int i=0;i<n;i++) {
		if (a[i]==1) {
			add(t[i],x[i]);
		} else if (a[i]==2) {
			add(t[i],-x[i]);
		} else {
			printf("%d\n", query(t[i],x[i])-query(t[i],-x[i]));
		}
	}
}


#include <bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define ll unsigned long long
#define ii pair<int,int>
#define mp make_pair
#define pb push_back
#define INF 1000000000
int n,a[100005],t[100005],x[100005];
vector<int> v;
tree<int,null_type,less<int>,splay_tree_tag,tree_order_statistics_node_update> s[200005];
int main() {
	scanf("%d",&n);
	for (int i=0;i<n;i++) {
		scanf("%d%d%d",&a[i],&t[i],&x[i]);
		v.pb(x[i]);
	}
	sort(all(v));
	for (int i=0;i<n;i++) {
		x[i]=(lower_bound(all(v),x[i])-v.begin());
	}
	int V=sz(v);
	for (int i=0;i<n;i++) {
		if (a[i]==1) {
			s[x[i]].insert(t[i]);
		} else if (a[i]==2) {
			s[x[i]+V].insert(t[i]);
		} else {
			printf("%d\n", s[x[i]].order_of_key(t[i])-s[x[i]+V].order_of_key(t[i]));
		}
	}
}


#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define ll unsigned long long
#define ii pair<int,int>
#define mp make_pair
#define pb push_back
#define INF 1000000000
int n,a[100005],t[100005],x[100005];
vector<int> v;
tree<int,null_mapped_type,less<int>,splay_tree_tag,tree_order_statistics_node_update> s[200005];
int main() {
	scanf("%d",&n);
	for (int i=0;i<n;i++) {
		scanf("%d%d%d",&a[i],&t[i],&x[i]);
		v.pb(x[i]);
	}
	sort(all(v));
	for (int i=0;i<n;i++) {
		x[i]=(lower_bound(all(v),x[i])-v.begin());
	}
	int V=sz(v);
	for (int i=0;i<n;i++) {
		if (a[i]==1) {
			s[x[i]].insert(t[i]);
		} else if (a[i]==2) {
			s[x[i]+V].insert(t[i]);
		} else {
			printf("%d\n", s[x[i]].order_of_key(t[i])-s[x[i]+V].order_of_key(t[i]));
		}
	}
}


#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define ll unsigned long long
#define ii pair<int,int>
#define mp make_pair
#define pb push_back
#define INF 1000000000
int n,a[100005],t[100005],x[100005];
vector<int> v;
tree<int,null_type,less<int>,splay_tree_tag,tree_order_statistics_node_update> s[200005];
int main() {
	scanf("%d",&n);
	for (int i=0;i<n;i++) {
		scanf("%d%d%d",&a[i],&t[i],&x[i]);
		v.pb(x[i]);
	}
	sort(all(v));
	for (int i=0;i<n;i++) {
		x[i]=(lower_bound(all(v),x[i])-v.begin());
	}
	int V=sz(v);
	for (int i=0;i<n;i++) {
		if (a[i]==1) {
			s[x[i]].insert(t[i]);
		} else if (a[i]==2) {
			s[x[i]+V].insert(t[i]);
		} else {
			printf("%d\n", s[x[i]].order_of_key(t[i])-s[x[i]+V].order_of_key(t[i]));
		}
	}
}


#include <bits/stdc++.h>
using namespace std;
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define ll unsigned long long
#define ii pair<int,int>
#define mp make_pair
#define pb push_back
#define INF 1000000000
int n,a[100005],t[100005],x[100005];
vector<int> v;
multiset<int> ft[100005];
void add(int p,int v) {
	for (;p<=n;p+=p&-p) ft[p].insert(v);
}
int query(int p,int v) {
	int r=0;
	for (;p;p-=p&-p) r+=ft[p].count(v);
	return r;
}
int main() {
	scanf("%d",&n);
	for (int i=0;i<n;i++) {
		scanf("%d%d%d",&a[i],&t[i],&x[i]);
		v.pb(t[i]);
	}
	sort(all(v));
	for (int i=0;i<n;i++) {
		t[i]=(lower_bound(all(v),t[i])-v.begin()+1);
	}
	for (int i=0;i<n;i++) {
		if (a[i]==1) {
			add(t[i],x[i]);
		} else if (a[i]==2) {
			add(t[i],-x[i]);
		} else {
			printf("%d\n", query(t[i],x[i])-query(t[i],-x[i]));
		}
	}
}


#include <bits/stdc++.h>

using namespace std;

#define SZ(x) ((int)(x).size())
#define PB(x) push_back(x)
#define MEMSET(x,v) memset(x,v,sizeof(x))
#define REP(i,n) for(int (i)=0;(i)<(n);++(i))
#define x first
#define y second
#define INF (0x3f3f3f3f)

typedef long long LL;
typedef pair<int, int> P2;
template<class A, class B> inline bool mina(A &x, B y) {return (x > y)?(x=y,1):0;}
template<class A, class B> inline bool maxa(A &x, B y) {return (x < y)?(x=y,1):0;}

// left: <= x
// right: > x
template<class T>
class SplayTree {
public:
    struct Node {
        Node *left, *right, *par;
        int sz;
        T key;
        int sum;
        int val;
        Node () {}
        Node (T v, int vv) {
            left = right = par = NULL;
            sz = 1;
            key = v;
            val = vv;
            sum = vv;
        }
        void push() {

        }
    };
    typedef Node *NPtr;
    NPtr root;
    SplayTree() { root = NULL; }
    SplayTree(NPtr r) { root = r; if(root) root->par = NULL; }
    void update_node (NPtr x) {
        x->sz = (x->left == NULL ? 0 : x->left->sz) +
                (x->right == NULL ? 0 : x->right->sz) + 1;
        x->sum = x->val + (x->left == NULL ? 0 : x->left->sum) +
                          (x->right == NULL ? 0 : x->right->sum);
    }
    // Rotate x up one level
    void rotate(NPtr x) {
        assert(x->par != NULL);
        NPtr p_x = x->par;
        NPtr pp_x = x->par->par;
        p_x->push();
        x->push();
        x->par = pp_x;
        if (pp_x) {
            if (pp_x->left == p_x) pp_x->left = x;
            else pp_x->right = x;
        }
        p_x->par = x;
        if (x == p_x->left) { // x is the left child
            if (x->right) x->right->par = p_x;
            p_x->left = x->right;
            x->right = p_x;
        } else { // x is the right child
            if (x->left) x->left->par = p_x;
            p_x->right = x->left;
            x->left = p_x;
        }
        update_node(p_x);
        update_node(x);
    }
    // If p == NULL, splay to the root. Otherwise, splay to the child of p
    void splay(NPtr x) { splay(x, NULL); }
    void splay(NPtr x, NPtr p) {
        while (x->par != p) {
            NPtr p_x = x->par;
            NPtr pp_x = x->par->par;
            if (pp_x == p) // zig
                rotate(x);
            else if ((pp_x->left == p_x) == (p_x->left == x)) // zig-zig
                rotate(p_x), rotate(x);
            else // zig-zag
                rotate(x), rotate(x);
        }
        if (!p) root = x;
    }
    void splayMax() {
        NPtr temp = root;
        while (temp && temp->right) temp = temp->right;
        if (temp) splay(temp);
    }
    void insert(T key, int x) {
        NPtr n = new Node(key, x);
        if (!root) {
            root = n;
        } else {
            NPtr t = root;
            while(t) {
                if (key > t->key) {
                    if (t->right) t = t->right;
                    else {
                        t->right = n;
                        break;
                    }
                } else {
                    if (t->left) t = t->left;
                    else {
                        t->left = n;
                        break;
                    }
                }
            }
            n->par = t;
        }
        splay(n, NULL);
    }
    int size() { return root ? root->sz : 0; }
    void join(SplayTree<T> &tree) {
        if (root) {
            splayMax();
            root->right = tree.root;
            if (tree.root) tree.root->par = root;
            update_node(root);
        } else {
            root = tree.root;
        }
    }

    void erase(T k) {
        if (contains(k)) {
            SplayTree<T> lc(root->left);
            SplayTree<T> rc(root->right);
            delete root;
            root = lc.root;
            join(rc);
        }
    }
    void erase_last(T k) {
        contains(k);
        SplayTree<T> lc(root->left);
        SplayTree<T> rc(root->right);
        delete root;
        root = lc.root;
        join(rc);
    }

    bool contains(T k) {
        NPtr t = find(root, k);
        if (t) {
            splay(t);
            return t->key == k;
        }
        return false;
    }
    T get_kth_smallest(int k) {
        assert(k >= 1 && k <= size());
        k--;
        NPtr t = root;
        while (t) {
            int x = size(t->left);
            if (k == x) {
                splay(t);
                return t->key;
            }
            else if (k < x) t = t->left;
            else {
                k -= x + 1;
                t = t->right;
            }
        }
        assert(false);
    }
    // Return the number of nodes whose key <= k
    int get_rank(T k) {
        NPtr t = find(root, k);
        if (t == NULL) return 0;
        else {
            splay(t);
            int p = root->val;
            //return p;
            if (root->left) p += root->left->sum;
            return p;
        }
    }
    void print() {
        if (root) {
            print(root);
            cout << ": " << root->sz << endl;
            printf("\n");
        }
    }
    void print(NPtr t) {
        if (t) {
            print(t->left);
            printf("%d,", t->key);
            print(t->right);
        }
    }
private:
    int size(NPtr t) { return t ? t->sz : 0; }
    // Return the right most node whose key <= k of the subtree t
    NPtr find(NPtr t, T k) {
        NPtr ret = NULL;
        while(t) {
            t->push();
            if (k == t->key) return t;
            else if (k < t->key) t = t->left;
            else {
                ret = t;
                t = t->right;
            }
        }
        return ret;
    }
};

const int MAXN = 100005;

SplayTree<int> trees[MAXN];
int queries[MAXN][3];
int main() {
    int Q;
    scanf("%d", &Q);
    map<int, int> id;
    REP(i, Q) {
        REP(j, 3) {
            scanf("%d", &queries[i][j]);
            id[queries[i][2]] = 1;
        }
    }
    int N = 0;
    for (auto it = id.begin(); it != id.end(); it++) {
        it->second = N++;
    }
    REP(i, Q) {
        int number = id[queries[i][2]];
        int tt = queries[i][1];
        int oper = queries[i][0];
        if (oper == 1) {
            trees[number].insert(tt, 1);
        } else if (oper == 2) {
            trees[number].insert(tt, -1);
        } else {
            printf("%d\n", trees[number].get_rank(tt));
        }
    }
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

#define SZ(x) ((int)(x).size())
#define PB(x) push_back(x)
#define MEMSET(x,v) memset(x,v,sizeof(x))
#define REP(i,n) for(int (i)=0;(i)<(n);++(i))
#define x first
#define y second
#define INF (0x3f3f3f3f)

typedef long long LL;
typedef pair<int, int> P2;
template<class A, class B> inline bool mina(A &x, B y) {return (x > y)?(x=y,1):0;}
template<class A, class B> inline bool maxa(A &x, B y) {return (x < y)?(x=y,1):0;}

// left: <= x
// right: > x
template<class T>
class SplayTree {
public:
    struct Node {
        Node *left, *right, *par;
        int sz;
        T key;
        Node () {}
        Node (T v) {
            left = right = par = NULL;
            sz = 1;
            key = v;
        }
        void push() {}
    };
    typedef Node *NPtr;
    NPtr root;
    SplayTree() { root = NULL; }
    SplayTree(NPtr r) { root = r; if(root) root->par = NULL; }
    void update_node (NPtr x) {
        x->sz = (x->left == NULL ? 0 : x->left->sz) +
                (x->right == NULL ? 0 : x->right->sz) + 1;
    }
    // Rotate x up one level
    void rotate(NPtr x) {
        assert(x->par != NULL);
        NPtr p_x = x->par;
        NPtr pp_x = x->par->par;
        p_x->push();
        x->push();
        x->par = pp_x;
        if (pp_x) {
            if (pp_x->left == p_x) pp_x->left = x;
            else pp_x->right = x;
        }
        p_x->par = x;
        if (x == p_x->left) { // x is the left child
            if (x->right) x->right->par = p_x;
            p_x->left = x->right;
            x->right = p_x;
        } else { // x is the right child
            if (x->left) x->left->par = p_x;
            p_x->right = x->left;
            x->left = p_x;
        }
        update_node(p_x);
        update_node(x);
    }
    // If p == NULL, splay to the root. Otherwise, splay to the child of p
    void splay(NPtr x) { splay(x, NULL); }
    void splay(NPtr x, NPtr p) {
        while (x->par != p) {
            NPtr p_x = x->par;
            NPtr pp_x = x->par->par;
            if (pp_x == p) // zig
                rotate(x);
            else if ((pp_x->left == p_x) == (p_x->left == x)) // zig-zig
                rotate(p_x), rotate(x);
            else // zig-zag
                rotate(x), rotate(x);
        }
        if (!p) root = x;
    }
    void splayMax() {
        NPtr temp = root;
        while (temp && temp->right) temp = temp->right;
        if (temp) splay(temp);
    }
    void insert(T key) {
        NPtr n = new Node(key);
        if (!root) {
            root = n;
        } else {
            NPtr t = root;
            while(t) {
                if (key > t->key) {
                    if (t->right) t = t->right;
                    else {
                        t->right = n;
                        break;
                    }
                } else {
                    if (t->left) t = t->left;
                    else {
                        t->left = n;
                        break;
                    }
                }
            }
            n->par = t;
        }
        splay(n, NULL);
    }
    int size() { return root ? root->sz : 0; }
    void join(SplayTree<T> &tree) {
        if (root) {
            splayMax();
            root->right = tree.root;
            if (tree.root) tree.root->par = root;
            update_node(root);
        } else {
            root = tree.root;
        }
    }

    void erase(T k) {
        if (contains(k)) {
            SplayTree<T> lc(root->left);
            SplayTree<T> rc(root->right);
            delete root;
            root = lc.root;
            join(rc);
        }
    }
    void erase_last(T k) {
        contains(k);
        SplayTree<T> lc(root->left);
        SplayTree<T> rc(root->right);
        delete root;
        root = lc.root;
        join(rc);
    }

    bool contains(T k) {
        NPtr t = find(root, k);
        if (t) {
            splay(t);
            return t->key == k;
        }
        return false;
    }
    T get_kth_smallest(int k) {
        assert(k >= 1 && k <= size());
        k--;
        NPtr t = root;
        while (t) {
            int x = size(t->left);
            if (k == x) {
                splay(t);
                return t->key;
            }
            else if (k < x) t = t->left;
            else {
                k -= x + 1;
                t = t->right;
            }
        }
        assert(false);
    }
    // Return the number of nodes whose key <= k
    int get_rank(T k) {
        NPtr t = find(root, k);
        if (t == NULL) return 0;
        else {
            splay(t);
            return size(root->left) + 1;
        }
    }
    void print() {
        if (root) {
            print(root);
            cout << ": " << root->sz << endl;
            printf("\n");
        }
    }
    void print(NPtr t) {
        if (t) {
            print(t->left);
            printf("%d,", t->key);
            print(t->right);
        }
    }
private:
    int size(NPtr t) { return t ? t->sz : 0; }
    // Return the right most node whose key <= k of the subtree t
    NPtr find(NPtr t, T k) {
        NPtr ret = NULL;
        while(t) {
            t->push();
            if (k == t->key) return t;
            else if (k < t->key) t = t->left;
            else {
                ret = t;
                t = t->right;
            }
        }
        return ret;
    }
};

const int MAXN = 1000005;

SplayTree<int> trees[MAXN];
int queries[MAXN][3];
int main() {
    int Q;
    scanf("%d", &Q);
    map<int, int> id;
    REP(i, Q) {
        REP(j, 3) {
            scanf("%d", &queries[i][j]);
            id[queries[i][2]] = 1;
        }
    }
    int N = 0;
    for (auto it = id.begin(); it != id.end(); it++) {
        it->second = N++;
    }
    REP(i, Q) {
        int number = id[queries[i][2]];
        int tt = queries[i][1];
        int oper = queries[i][0];
        if (oper == 1) {
            trees[number].insert(tt);
        } else if (oper == 2) {
            trees[number].erase_last(tt);
        } else {
            printf("%d\n", trees[number].get_rank(tt));
        }
    }
    return 0;
}


#include <bits/stdc++.h>
using namespace std;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> Tree;

int q;
map<int,Tree> times;

int main() {
	scanf("%d",&q);
	while (q--) {
		int a,t,x;
		scanf("%d%d%d",&a,&t,&x);
		if (a==1) {
			times[x].insert(t);
		} else if (a==2) {
			auto it = times[x].find_by_order(times[x].order_of_key(t)-1);
			times[x].erase(it);
		} else {
			printf("%d\n",times[x].order_of_key(t));
		}
	}

	return 0;
}


#include <bits/stdc++.h>
using namespace std;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> BBST;

int q;
map<int,BBST> add;
map<int,BBST> sub;

int main() {
	scanf("%d",&q);
	while (q--) {
		int a,t,x;
		scanf("%d%d%d",&a,&t,&x);
		if (a==1) {
			add[x].insert(t);
		} else if (a==2) {
			sub[x].insert(t);
		} else {
			printf("%d\n",add[x].order_of_key(t)-sub[x].order_of_key(t));
		}
	}

	return 0;
}


#include <bits/stdc++.h>
using namespace std;

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> Tree;

int q;
map<int,Tree> add;
map<int,Tree> sub;

int main() {
	scanf("%d",&q);
	while (q--) {
		int a,t,x;
		scanf("%d%d%d",&a,&t,&x);
		if (a==1) {
			add[x].insert(t);
		} else if (a==2) {
			sub[x].insert(t);
		} else {
			printf("%d\n",add[x].order_of_key(t)-sub[x].order_of_key(t));
		}
	}

	return 0;
}


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;

const int N = 100005;

int a[N], t[N], x[N];

class SegmTree {
public:
    struct Node {
        int sum;
        int lson, rson;
        Node() {
            sum = 0;
            lson = rson = -1;
        }
    };

    vector<Node> nd;

    void init() {
        nd.push_back(Node());
    }

    void ml(int v) {
        if (nd[v].lson != -1) return;
        nd.push_back(Node());
        nd[v].lson = nd.size() - 1;

    }

    void mr(int v) {
        if (nd[v].rson != -1) return;
        nd.push_back(Node());
        nd[v].rson = nd.size() - 1;
    }

    void add(int v, int l, int r, int qid, int val) {
        if (l == r) {
            nd[v].sum += val;
            return;
        }
        int m = (l + r) >> 1;
        if (qid <= m) {
            ml(v);
            add(nd[v].lson, l, m, qid, val);
        } else {
            mr(v);
            add(nd[v].rson, m + 1, r, qid, val);
        }
        nd[v].sum += val;
    }

    int get(int v, int l, int r, int ql, int qr) {
        if (v == -1) return 0;
        if (ql > r || qr < l) return 0;
        if (ql <= l && qr >= r) return nd[v].sum;
        int m = (l + r) >> 1;
        return get(nd[v].lson, l, m, ql, qr) + get(nd[v].rson, m + 1, r, ql, qr);
    }
};

SegmTree st[N];

int main() {
    int n;
    scanf("%d", &n);
    set<int> vals;
    set<int> ts;
    for (int i = 0; i < n; ++i) {
        scanf("%d%d%d", &a[i], &t[i], &x[i]);
        vals.insert(x[i]);
        ts.insert(t[i]);
    }
    map<int, int> nt, nval;
    int curt = 0;
    for (auto t : ts) {
        nt[t] = ++curt;
    }
    int curv = 0;
    for (auto val : vals) {
        nval[val] = ++curv;
    }
    for (int i = 0; i < n; ++i) {
        t[i] = nt[t[i]];
        x[i] = nval[x[i]];
    }
    for (int i = 1; i <= curv; ++i) {
        st[i].init();
    }
    for (int i = 0; i < n; ++i) {
        if (a[i] == 1) {
            st[x[i]].add(0, 1, curt, t[i], 1);
        } else if (a[i] == 2) {
            st[x[i]].add(0, 1, curt, t[i], -1);
        } else {
            printf("%d\n", st[x[i]].get(0, 1, curt, 1, t[i]));
        }
    }
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;

const int N = 1000005;

struct Quest {
    int a, t, x;
};

bool operator<(Quest a, Quest b) {
    return a.t < b.t;
}

Quest q[N];
unordered_map<int, int> cnt;

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%d%d%d", &q[i].a, &q[i].t, &q[i].x);
    }
    sort(q, q + n);
    for (int i = 0; i < n; ++i) {
        if (q[i].a == 1) {
            ++cnt[q[i].x];
        } else if (q[i].a == 2) {
            --cnt[q[i].x];
        } else {
            printf("%d\n", cnt[q[i].x]);
        }
    }
    return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <string>
#include <utility>
#include <cstring>
#include <cassert>
#include <bitset>
#include <cmath>
#include <stack>
#include <queue>

using namespace std;

typedef long long ll;
typedef pair<int, int> ii;

const int MAXN = 100010;
const int MXLOG = 30;
const int mod = 1000000007;
const int inf = (1 << 30);
const ll infll = (1LL << 60);

map<int, int> s;
struct query
{
	int t, x, type, index;
	query() {}
	query(int T, int X, int TY, int IN)
	{
		t = T, x = X, type = TY, index = IN;
	}
	bool operator<(const query &rhs) const
	{
		if (rhs.type == 3)
		{
			if (type == 3) return t < rhs.t;
			if (rhs.t < t) return false;
			return index < rhs.index;
		}
		if (type == 3)
			return !(rhs < *this);
		if (t == rhs.t)
			return type < rhs.type;
		return t < rhs.t;
	}
} ar[MAXN];
int n;
int ans[MAXN];

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		int type, t, x;
		scanf("%d%d%d", &type, &t, &x);
		ar[i] = query(t, x, type, i);
	}
	sort(ar + 1, ar + 1 + n);
	memset(ans, -1, sizeof ans);
	for (int i = 1; i <= n; i++)
	{
		int type = ar[i].type, t = ar[i].t, x = ar[i].x;
		if (type == 1)
			s[x]++;
		else if (type == 2)
			s[x]--;
		else ans[ar[i].index] = s[x];
	}
	for (int i = 1; i <= n; i++)
	{
		if (ans[i] != -1)
			printf("%d\n", ans[i]);
	}
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <string>
#include <utility>
#include <cstring>
#include <cassert>
#include <bitset>
#include <cmath>
#include <stack>
#include <queue>

using namespace std;

typedef long long ll;
typedef pair<int, int> ii;

const int MAXN = 100010;
const int MXLOG = 30;
const int mod = 1000000007;
const int inf = (1 << 30);
const ll infll = (1LL << 60);

map<int, int> s;
struct query
{
	int t, x, type, index;
	query() {}
	query(int T, int X, int TY, int IN)
	{
		t = T, x = X, type = TY, index = IN;
	}
	bool operator<(const query &rhs) const
	{
		if (rhs.type == 3 || type == 3)
			return index < rhs.index;
		if (t == rhs.t)
			return type < rhs.type;
		return t < rhs.t;
	}
} ar[MAXN];
int n;

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		int type, t, x;
		scanf("%d%d%d", &type, &t, &x);
		ar[i] = query(t, x, type, i);
	}
	sort(ar, ar + n);
	for (int i = 1; i <= n; i++)
	{
		int type = ar[i].type, t = ar[i].t, x = ar[i].x;
		if (type == 1)
			s[x]++;
		else if (type == 2)
			s[x]--;
		else printf("%d\n", s[x]);
	}
}


#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 350;
int a[N], t[N], x[N];
int dist[N], disx[N];
int n, lent, lenx;
map<int, int> mp[M], mp2[N];
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%d%d%d", &a[i], &t[i], &x[i]);
		dist[i] = t[i];
		disx[i] = x[i];
	}
	sort(dist + 1, dist + 1 + n);
	sort(disx + 1, disx + 1 + n);
	lent = unique(dist + 1, dist + 1 + n) - dist - 1;
	lenx = unique(disx + 1, disx + 1 + n) - disx - 1;
	for(int i = 1; i <= n; ++i)
	{
		t[i] = lower_bound(dist + 1, dist + 1 + lent, t[i]) - dist - 1;
		x[i] = lower_bound(disx + 1, disx + 1 + lenx, x[i]) - disx - 1;
	}
	for(int i = 1; i <= n; ++i)
	{
		if(a[i] != 3)
		{
			int L = (t[i] - 1) / M + 1;
			int R = lent / M;
			for(int j = L; j <= R; ++j)
				mp[j][x[i]] += (a[i] == 1 ? 1 : -1);
			int lim = L * M;
			for(int j = t[i]; j <= lim; ++j)
				mp2[j][x[i]] += (a[i] == 1 ? 1 : -1);
		}
		else
		{
			printf("%d\n", mp2[t[i]][x[i]] + mp[(t[i] - 1) / M + 1]
			[x[i]]);
		}
	}
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N], t[N], x[N];
int dist[N], disx[N];
map <int, int> mp[1 << 18];
int n, lent, lenx;
void update(int x, int tl, int tr, int L, int y, int z)
{
	if(L <= tl)
	{
		mp[x][z] += y;
		return;
	}
	int mid = (tl + tr) >> 1;
	if(L < mid)
		update(x << 1, tl, mid, L, y, z);
	update(x << 1 | 1, mid + 1, tr, L, y, z);
}
int query(int x, int tl, int tr, int now, int z)
{
	if(tl == tr)
		return mp[x][z];
	int mid = (tl + tr) >> 1;
	if(now <= mid)
		return mp[x][z] + query(x << 1, tl, mid, now, z);
	else
		return mp[x][z] + query(x << 1 | 1, mid + 1, tr, now, z);
}
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%d%d%d", &a[i], &t[i], &x[i]);
		dist[i] = t[i];
		disx[i] = x[i];
	}
	sort(dist + 1, dist + 1 + n);
	sort(disx + 1, disx + 1 + n);
	lent = unique(dist + 1, dist + 1 + n) - dist - 1;
	lenx = unique(disx + 1, disx + 1 + n) - disx - 1;
	for(int i = 1; i <= n; ++i)
	{
		t[i] = lower_bound(dist + 1, dist + 1 + lent, t[i]) - dist;
		x[i] = lower_bound(disx + 1, disx + 1 + lenx, x[i]) - disx;
	}
	for(int i = 1; i<= n; ++i)
	{
		if(a[i] != 3)
			update(1, 1, lent, t[i], a[i] == 1 ? 1 : -1, x[i]);
		else
			printf("%d\n", query(1, 1, lent, t[i], x[i]));
	}
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 500, M0 = 210;
int a[N], t[N], x[N];
int dist[N], disx[N];
int n, lent, lenx;
map<int, int> mp[M0], mp2[N];
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%d%d%d", &a[i], &t[i], &x[i]);
		dist[i] = t[i];
		disx[i] = x[i];
	}
	sort(dist + 1, dist + 1 + n);
	sort(disx + 1, disx + 1 + n);
	lent = unique(dist + 1, dist + 1 + n) - dist - 1;
	lenx = unique(disx + 1, disx + 1 + n) - disx - 1;
	for(int i = 1; i <= n; ++i)
	{
		t[i] = lower_bound(dist + 1, dist + 1 + lent, t[i]) - dist;
		x[i] = lower_bound(disx + 1, disx + 1 + lenx, x[i]) - disx;
	}
	for(int i = 1; i <= n; ++i)
	{
		if(a[i] != 3)
		{
			int L = (t[i] - 1) / M + 2;
			int R = lent / M;
			for(int j = L; j <= R; ++j)
				mp[j][x[i]] += (a[i] == 1 ? 1 : -1);
			int lim = (L - 1) * M;
			if(t[i] <= lim)
				mp2[t[i]][x[i]] += (a[i] == 1 ? 1 : -1);
		}
		else
		{
			int ans = 0;
			for(int j = ((t[i] - 1) / M)* M + 1; j <= t[i]; ++j)
				ans += mp2[j][x[i]];
			ans += mp[(t[i] - 1) / M + 1][x[i]];
			printf("%d\n", ans);
		}
	}
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 350;
int a[N], t[N], x[N];
int dist[N], disx[N];
int n, lent, lenx;
map<int, int> mp[M], mp2[N];
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%d%d%d", &a[i], &t[i], &x[i]);
		dist[i] = t[i];
		disx[i] = x[i];
	}
	sort(dist + 1, dist + 1 + n);
	sort(disx + 1, disx + 1 + n);
	lent = unique(dist + 1, dist + 1 + n) - dist - 1;
	lenx = unique(disx + 1, disx + 1 + n) - disx - 1;
	for(int i = 1; i <= n; ++i)
	{
		t[i] = lower_bound(dist + 1, dist + 1 + lent, t[i]) - dist;
		x[i] = lower_bound(disx + 1, disx + 1 + lenx, x[i]) - disx;
	}
	for(int i = 1; i <= n; ++i)
	{
		if(a[i] != 3)
		{
			int L = (t[i] - 1) / M + 2;
			int R = lent / M;
			for(int j = L; j <= R; ++j)
				mp[j][x[i]] += (a[i] == 1 ? 1 : -1);
			int lim = (L - 1) * M;
			if(t[i] <= lim)
				mp2[t[i]][x[i]] += (a[i] == 1 ? 1 : -1);
		}
		else
		{
			int ans = 0;
			for(int j = ((t[i] - 1) / M)* M + 1; j <= t[i]; ++j)
				ans += mp2[j][x[i]];
			ans += mp[(t[i] - 1) / M + 1][x[i]];
			printf("%d\n", ans);
		}
	}
	return 0;
}


#include <iostream>
#include <cstdio>
#include <climits>
#include <algorithm>

using namespace std;
typedef pair<int, int> P;

struct Q {
	int a;
	int t;
	int x;
};
Q q[1 << 20];
pair<P, int> X[1 << 20];
pair<int, P> E[1 << 20];
int es;

int eind(int x) {
	int A = 0;
	int B = es - 1;
	while(A != B) {
		int M = (A + B) / 2;
		if(E[M].first < x) {
			A = M + 1;
		} else {
			B = M;
		}
	}
	return A;
}

void ch(int x, int t, int d) {
	int ei = eind(x);
	int a = E[ei].second.first;
	int b = E[ei].second.second;

	int A = 0;
	int B = b - a - 1;
	while(A != B) {
		int M = (A + B) / 2;
		if(X[a + M].first.second < t) {
			A = M + 1;
		} else {
			B = M;
		}
	}
	int i = A + 1;
	while(i <= b - a) {
		X[a + i - 1].second += d;
		i += (i & -i);
	}
}

int sum(int x, int t) {
	int ei = eind(x);
	int a = E[ei].second.first;
	int b = E[ei].second.second;

	int A = 0;
	int B = b - a - 1;
	while(A != B) {
		int M = (A + B) / 2;
		if(X[a + M].first.second < t) {
			A = M + 1;
		} else {
			B = M;
		}
	}
	int i = A + 1;
	int ret = 0;
	while(i != 0) {
		ret += X[a + i - 1].second;
		i -= (i & -i);
	}
	return ret;
}

int main() {
	int n;
	cin >> n;

	for(int i = 0; i < n; ++i) {
		scanf("%d %d %d", &q[i].a, &q[i].t, &q[i].x);
	}

	for(int i = 0; i < n; ++i) {
		X[i] = make_pair(P(q[i].x, q[i].t), 0);
		E[i] = make_pair(q[i].x, P(INT_MAX, INT_MIN));
	}
	sort(X, X + n);
	sort(E, E + n);
	es = unique(E, E + n) - E;

	int ei = 0;
	for(int i = 0; i < n; ++i) {
		while(E[ei].first != X[i].first.first) ++ei;
		E[ei].second.first = min(E[ei].second.first, i);
		E[ei].second.second = max(E[ei].second.second, i + 1);
	}

	for(int i = 0; i < n; ++i) {
		if(q[i].a == 1) {
			ch(q[i].x, q[i].t, 1);
		}
		if(q[i].a == 2) {
			ch(q[i].x, q[i].t, -1);
		}
		if(q[i].a == 3) {
			printf("%d\n", sum(q[i].x, q[i].t));
		}
	}

	return 0;
}


#include <iostream>
#include <cstdio>
#include <climits>
#include <algorithm>

using namespace std;
typedef pair<int, int> P;

struct Q {
	int a;
	int t;
	int x;
};
Q q[1 << 20];
pair<P, int> X[1 << 20];
pair<int, P> E[1 << 20];
int es;

int eind(int x) {
	int A = 0;
	int B = es - 1;
	while(A != B) {
		int M = (A + B) / 2;
		if(E[M].first < x) {
			A = M + 1;
		} else {
			B = M;
		}
	}
	return A;
}

void ch(int x, int t, int d) {
	int ei = eind(x);
	int a = E[ei].second.first;
	int b = E[ei].second.second;

	int A = 0;
	int B = b - a - 1;
	while(A != B) {
		int M = (A + B) / 2;
		if(X[a + M].first.second < x) {
			A = M + 1;
		} else {
			B = M;
		}
	}
	int i = A + 1;
	while(i <= b - a) {
		X[a + i - 1].second += d;
		i += (i & -i);
	}
}

int sum(int x, int t) {
	int ei = eind(x);
	int a = E[ei].second.first;
	int b = E[ei].second.second;

	int A = 0;
	int B = b - a - 1;
	while(A != B) {
		int M = (A + B) / 2;
		if(X[a + M].first.second < x) {
			A = M + 1;
		} else {
			B = M;
		}
	}
	int i = A + 1;
	int ret = 0;
	while(i != 0) {
		ret += X[a + i - 1].second;
		i -= (i & -i);
	}
	return ret;
}

int main() {
	int n;
	cin >> n;

	for(int i = 0; i < n; ++i) {
		scanf("%d %d %d", &q[i].a, &q[i].t, &q[i].x);
	}

	for(int i = 0; i < n; ++i) {
		X[i] = make_pair(P(q[i].x, q[i].t), 0);
		E[i] = make_pair(q[i].x, P(INT_MAX, INT_MIN));
	}
	sort(X, X + n);
	sort(E, E + n);
	es = unique(E, E + n) - E;

	int ei = 0;
	for(int i = 0; i < n; ++i) {
		while(E[ei].first != X[i].first.first) ++ei;
		E[ei].second.first = min(E[ei].second.first, i);
		E[ei].second.second = max(E[ei].second.second, i + 1);
	}

	for(int i = 0; i < n; ++i) {
		if(q[i].a == 1) {
			ch(q[i].x, q[i].t, 1);
		}
		if(q[i].a == 2) {
			ch(q[i].x, q[i].t, -1);
		}
		if(q[i].a == 3) {
			printf("%d\n", sum(q[i].x, q[i].t));
		}
	}

	return 0;
}


#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <string>
#include <cmath>
#include <stack>
#define ll long long
#define ull unsigned long long
#define exp 0.00000001
using namespace std;
const int mxx = 1e6+5;
const int mx = 1e9+5;
map<int, int>ma[mxx];///ma[每个数字b(第几个数)][时间轴]；
map<int, int>mma;///每个数字b离散为第几个没出现的数
///树状数组模板。。。
int bit(int x)
{
    return x&(-x);
}

void up(int x, int y, int k)
{
    while (x < mx)
    {
        ma[y][x] += k;///k为1时数字进来,-1时代表出去。。。。
        x += bit(x);
    }
    return;
}

int qry(int x, int y)
{
    int ans = 0;
    while (x)
    {
        ans += ma[y][x];
        x -= bit(x);
    }
    return ans;
}
int main()
{
    int n, t, a, b, cnt = 1;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i)
    {
        scanf("%d%d%d", &t, &a, &b);
        if (!mma[b] && t == 1) mma[b] = cnt++;///每种数字，这就算是简略的离散了
        if (t == 1)
            up(a, mma[b], 1);
        else if (t == 2)
            up(a, mma[b], -1);
        else printf("%d\n", qry(a, mma[b]));
    }
   return 0;
}
//hellobig_tq


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const ll linf=0x3f3f3f3f3f3f3f3f;

int mi[maxn*4],ma[maxn<<2],lazy[maxn*4],arr[maxn],ans[maxn],cnt=1;
int qv;
int ql,qr;
int n,st=1;

void build(int o,int l,int r)
{
    if(l==r)
    {
        mi[o]=0;
        ma[o]=0;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    build(lo,l,mid);
    build(ro,mid+1,r);
}

void update(int o,int l,int r)
{
    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    mi[lo]+=lazy[o];
    ma[lo]+=lazy[o];
    mi[ro]+=lazy[o];
    ma[ro]+=lazy[o];
    lazy[lo]+=lazy[o];
    lazy[ro]+=lazy[o];
    lazy[o]=0;
}

void add(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        mi[o]+=qv;
        ma[o]+=qv;
        lazy[o]+=qv;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    if(mid>=ql)add(lo,l,mid);
    if(mid<qr)add(ro,mid+1,r);
    mi[o]=min(mi[lo],mi[ro]);
    ma[o]=max(ma[lo],ma[ro]);
}

int querymin(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return mi[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymin(lo,l,mid);
    if(mid<qr)b=querymin(ro,mid+1,r);
    return min(a,b);
}
int querymax(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return ma[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymax(lo,l,mid);
    if(mid<qr)b=querymax(ro,mid+1,r);
    return max(a,b);
}

void check()
{
//    cout<<endl;
//    cout<<mi[1]<<' ';
    qr=ql=n;
    if(mi[1]<0||querymin(1,1,n)!=0)
    {
        ans[cnt++]=-1;
        return;
    }
    ql=1;
    ans[cnt++]=querymax(1,1,n);
}

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d\n",&n);
    build(1,1,n);
    for(int i=0;i<n;i++)
    {
        char op;
        scanf("%c",&op);
        //cout<<op<<endl;
        if(op=='R')
        {
            st++;
            ans[cnt++]=ans[cnt-2];
        }
        else if(op=='L')
        {
            st--;
            ans[cnt++]=ans[cnt-2];
        }
        else
        {
            ql=st,qr=n;
            if(op=='(')
            {
                qv=1-arr[st];
                arr[st]=1;
            }
            else if(op==')')
            {
                qv=-1-arr[st];
                arr[st]=-1;
            }
            else
            {
                qv=-arr[st];
                arr[st]=0;
            }
            add(1,1,n);
            check();
        }
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d",ans[i]);
        if(i!=n)printf(" ");
    }

    return 0;
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const ll linf=0x3f3f3f3f3f3f3f3f;

int mi[maxn*4],ma[maxn<<2],lazy[maxn*4],arr[maxn],ans[maxn],cnt=1;
int qv;
int ql,qr;
int n,st=1;

void build(int o,int l,int r)
{
    if(l==r)
    {
        mi[o]=0;
        ma[o]=0;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    build(lo,l,mid);
    build(ro,mid+1,r);
}

void update(int o,int l,int r)
{
    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    mi[lo]+=lazy[o];
    ma[lo]+=lazy[o];
    mi[ro]+=lazy[o];
    ma[ro]+=lazy[o];
    lazy[lo]+=lazy[o];
    lazy[ro]+=lazy[o];
    lazy[o]=0;
}

void add(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        mi[o]+=qv;
        ma[o]+=qv;
        lazy[o]+=qv;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    if(mid>=ql)add(lo,l,mid);
    if(mid<qr)add(ro,mid+1,r);
    mi[o]=min(mi[lo],mi[ro]);
    ma[o]=max(ma[lo],ma[ro]);
}

int querymin(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return mi[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymin(lo,l,mid);
    if(mid<qr)b=querymin(ro,mid+1,r);
    return min(a,b);
}
int querymax(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return ma[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymax(lo,l,mid);
    if(mid<qr)b=querymax(ro,mid+1,r);
    return max(a,b);
}

void check()
{
//    cout<<endl;
//    cout<<mi[1]<<' ';
    qr=ql=n;
    if(mi[1]<0||querymin(1,1,n)!=0)
    {
        ans[cnt++]=-1;
        return;
    }
    ql=1;
    ans[cnt++]=querymax(1,1,n);
}

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d",&n);
    getchar();
    build(1,1,n);
    for(int i=0;i<n;i++)
    {
        char op;
        op=getchar();
        //cout<<op<<endl;
        if(op=='R')
        {
            st++;
            ans[cnt++]=ans[cnt-2];
        }
        else if(op=='L')
        {
            st--;
            ans[cnt++]=ans[cnt-2];
        }
        else
        {
            ql=st,qr=n;
            if(op=='(')
            {
                qv=1-arr[st];
                arr[st]=1;
            }
            else if(op==')')
            {
                qv=-1-arr[st];
                arr[st]=-1;
            }
            else
            {
                qv=-arr[st];
                arr[st]=0;
            }
            add(1,1,n);
            check();
        }
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d",ans[i]);
        if(i!=n)printf(" ");
    }

    return 0;
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const ll linf=0x3f3f3f3f3f3f3f3f;

int mi[maxn*4]={0},ma[maxn<<2]={0},lazy[maxn*4]={0},arr[maxn]={0},ans[maxn]={0},cnt=1;
int qv;
int ql,qr;
int n,st=1;
char op[maxn]={0};

void update(int o,int l,int r)
{
    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    mi[lo]+=lazy[o];
    ma[lo]+=lazy[o];
    mi[ro]+=lazy[o];
    ma[ro]+=lazy[o];
    lazy[lo]+=lazy[o];
    lazy[ro]+=lazy[o];
    lazy[o]=0;
}

void add(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        mi[o]+=qv;
        ma[o]+=qv;
        lazy[o]+=qv;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    if(mid>=ql)add(lo,l,mid);
    if(mid<qr)add(ro,mid+1,r);
    mi[o]=min(mi[lo],mi[ro]);
    ma[o]=max(ma[lo],ma[ro]);
}

int querymin(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return mi[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a=inf,b=inf;
    if(mid>=ql)a=querymin(lo,l,mid);
    if(mid<qr)b=querymin(ro,mid+1,r);
    return min(a,b);
}
int querymax(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return ma[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a=-inf,b=-inf;
    if(mid>=ql)a=querymax(lo,l,mid);
    if(mid<qr)b=querymax(ro,mid+1,r);
    return max(a,b);
}

void check()
{
//    cout<<endl;
//    cout<<mi[1]<<' ';
    qr=ql=n;
    if(mi[1]<0||querymin(1,1,n)!=0)
    {
        ans[cnt++]=-1;
        return;
    }
    ql=1;
    ans[cnt++]=querymax(1,1,n);
}

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d%s",&n,op);
    for(int i=0;i<n;i++)
    {
        //cout<<op<<endl;
        if(op[i]=='R')
        {
            st++;
            ans[cnt]=ans[cnt-1];
            cnt++;
        }
        else if(op[i]=='L')
        {
            if(st!=1)st--;
            ans[cnt]=ans[cnt-1];
            cnt++;
        }
        else
        {
            ql=st,qr=n;
            if(op[i]=='(')
            {
                qv=1-arr[st];
                arr[st]=1;
            }
            else if(op[i]==')')
            {
                qv=-1-arr[st];
                arr[st]=-1;
            }
            else
            {
                qv=-arr[st];
                arr[st]=0;
            }
            add(1,1,n);
            check();
        }
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d",ans[i]);
        if(i!=n)printf(" ");
    }

    return 0;
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e2+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const ll linf=0x3f3f3f3f3f3f3f3f;

int mi[maxn*4]={0},ma[maxn<<2]={0},lazy[maxn*4]={0},arr[maxn]={0},ans[maxn]={0},cnt=1;
int qv;
int ql,qr;
int n,st=1;
char op[maxn]={0};

void build(int o,int l,int r)
{
    if(l==r)
    {
        mi[o]=0;
        ma[o]=0;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    build(lo,l,mid);
    build(ro,mid+1,r);
}

void update(int o,int l,int r)
{
    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    mi[lo]+=lazy[o];
    ma[lo]+=lazy[o];
    mi[ro]+=lazy[o];
    ma[ro]+=lazy[o];
    lazy[lo]+=lazy[o];
    lazy[ro]+=lazy[o];
    lazy[o]=0;
}

void add(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        mi[o]+=qv;
        ma[o]+=qv;
        lazy[o]+=qv;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    if(mid>=ql)add(lo,l,mid);
    if(mid<qr)add(ro,mid+1,r);
    mi[o]=min(mi[lo],mi[ro]);
    ma[o]=max(ma[lo],ma[ro]);
}

int querymin(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return mi[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymin(lo,l,mid);
    if(mid<qr)b=querymin(ro,mid+1,r);
    return min(a,b);
}
int querymax(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return ma[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymax(lo,l,mid);
    if(mid<qr)b=querymax(ro,mid+1,r);
    return max(a,b);
}

void check()
{
//    cout<<endl;
//    cout<<mi[1]<<' ';
    qr=ql=n;
    if(mi[1]<0||querymin(1,1,n)!=0)
    {
        ans[cnt++]=-1;
        return;
    }
    ql=1;
    ans[cnt++]=querymax(1,1,n);
}

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d%s",&n,op);
    build(1,1,n);
    for(int i=0;i<n;i++)
    {
        //cout<<op<<endl;
        if(op[i]=='R')
        {
            st++;
            ans[cnt++]=ans[cnt-2];
        }
        else if(op[i]=='L')
        {
            st--;
            ans[cnt++]=ans[cnt-2];
        }
        else
        {
            ql=st,qr=n;
            if(op[i]=='(')
            {
                qv=1-arr[st];
                arr[st]=1;
            }
            else if(op[i]==')')
            {
                qv=-1-arr[st];
                arr[st]=-1;
            }
            else
            {
                qv=-arr[st];
                arr[st]=0;
            }
            add(1,1,n);
            check();
        }
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d",ans[i]);
        if(i!=n)printf(" ");
    }

    return 0;
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const ll linf=0x3f3f3f3f3f3f3f3f;

int mi[maxn*4],ma[maxn<<2],lazy[maxn*4],arr[maxn],ans[maxn],cnt=1;
int qv;
int ql,qr;
int n,st=1;

void build(int o,int l,int r)
{
    if(l==r)
    {
        mi[o]=0;
        ma[o]=0;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    build(lo,l,mid);
    build(ro,mid+1,r);
}

void update(int o,int l,int r)
{
    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    mi[lo]+=lazy[o];
    ma[lo]+=lazy[o];
    mi[ro]+=lazy[o];
    ma[ro]+=lazy[o];
    lazy[lo]+=lazy[o];
    lazy[ro]+=lazy[o];
    lazy[o]=0;
}

void add(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        mi[o]+=qv;
        ma[o]+=qv;
        lazy[o]+=qv;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    if(mid>=ql)add(lo,l,mid);
    if(mid<qr)add(ro,mid+1,r);
    mi[o]=min(mi[lo],mi[ro]);
    ma[o]=max(ma[lo],ma[ro]);
}

int querymin(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return mi[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymin(lo,l,mid);
    if(mid<qr)b=querymin(ro,mid+1,r);
    return min(a,b);
}
int querymax(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return ma[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymax(lo,l,mid);
    if(mid<qr)b=querymax(ro,mid+1,r);
    return max(a,b);
}

void check()
{
//    cout<<endl;
//    cout<<mi[1]<<' ';
    qr=ql=n;
    if(mi[1]<0||querymin(1,1,n)!=0)
    {
        ans[cnt++]=-1;
        return;
    }
    ql=1;
    ans[cnt++]=querymax(1,1,n);
}

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d\n",&n);
    build(1,1,n);
    for(int i=0;i<n;i++)
    {
        char op;
        op=getchar();
        //cout<<op<<endl;
        if(op=='R')
        {
            st++;
            ans[cnt++]=ans[cnt-2];
        }
        else if(op=='L')
        {
            st--;
            ans[cnt++]=ans[cnt-2];
        }
        else
        {
            ql=st,qr=n;
            if(op=='(')
            {
                qv=1-arr[st];
                arr[st]=1;
            }
            else if(op==')')
            {
                qv=-1-arr[st];
                arr[st]=-1;
            }
            else
            {
                qv=-arr[st];
                arr[st]=0;
            }
            add(1,1,n);
            check();
        }
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d",ans[i]);
        if(i!=n)printf(" ");
    }

    return 0;
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const ll linf=0x3f3f3f3f3f3f3f3f;

int mi[maxn*4]={0},ma[maxn<<2]={0},lazy[maxn*4]={0},arr[maxn]={0},ans[maxn]={0},cnt=1;
int qv;
int ql,qr;
int n,st=1;
char op[maxn]={0};

void build(int o,int l,int r)
{
    if(l==r)
    {
        mi[o]=0;
        ma[o]=0;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    build(lo,l,mid);
    build(ro,mid+1,r);
}

void update(int o,int l,int r)
{
    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    mi[lo]+=lazy[o];
    ma[lo]+=lazy[o];
    mi[ro]+=lazy[o];
    ma[ro]+=lazy[o];
    lazy[lo]+=lazy[o];
    lazy[ro]+=lazy[o];
    lazy[o]=0;
}

void add(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        mi[o]+=qv;
        ma[o]+=qv;
        lazy[o]+=qv;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    if(mid>=ql)add(lo,l,mid);
    if(mid<qr)add(ro,mid+1,r);
    mi[o]=min(mi[lo],mi[ro]);
    ma[o]=max(ma[lo],ma[ro]);
}

int querymin(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return mi[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymin(lo,l,mid);
    if(mid<qr)b=querymin(ro,mid+1,r);
    return min(a,b);
}
int querymax(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return ma[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymax(lo,l,mid);
    if(mid<qr)b=querymax(ro,mid+1,r);
    return max(a,b);
}

void check()
{
//    cout<<endl;
//    cout<<mi[1]<<' ';
    qr=ql=n;
    if(mi[1]<0||querymin(1,1,n)!=0)
    {
        ans[cnt++]=-1;
        return;
    }
    ql=1;
    ans[cnt++]=querymax(1,1,n);
}

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d\n%s",&n,op);
    build(1,1,n);
    for(int i=0;i<n;i++)
    {
        //cout<<op<<endl;
        if(op[i]=='R')
        {
            st++;
            ans[cnt++]=ans[cnt-2];
        }
        else if(op[i]=='L')
        {
            st--;
            ans[cnt++]=ans[cnt-2];
        }
        else
        {
            ql=st,qr=n;
            if(op[i]=='(')
            {
                qv=1-arr[st];
                arr[st]=1;
            }
            else if(op[i]==')')
            {
                qv=-1-arr[st];
                arr[st]=-1;
            }
            else
            {
                qv=-arr[st];
                arr[st]=0;
            }
            add(1,1,n);
            check();
        }
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d",ans[i]);
        if(i!=n)printf(" ");
    }

    return 0;
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const ll linf=0x3f3f3f3f3f3f3f3f;

int mi[maxn*4],ma[maxn<<2],lazy[maxn*4],arr[maxn],ans[maxn],cnt=1;
int qv;
int ql,qr;
int n,st=1;

void build(int o,int l,int r)
{
    if(l==r)
    {
        mi[o]=0;
        ma[o]=0;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    build(lo,l,mid);
    build(ro,mid+1,r);
}

void update(int o,int l,int r)
{
    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    mi[lo]+=lazy[o];
    ma[lo]+=lazy[o];
    mi[ro]+=lazy[o];
    ma[ro]+=lazy[o];
    lazy[lo]+=lazy[o];
    lazy[ro]+=lazy[o];
    lazy[o]=0;
}

void add(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        mi[o]+=qv;
        ma[o]+=qv;
        lazy[o]+=qv;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    if(mid>=ql)add(lo,l,mid);
    if(mid<qr)add(ro,mid+1,r);
    mi[o]=min(mi[lo],mi[ro]);
    ma[o]=max(ma[lo],ma[ro]);
}

int querymin(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return mi[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymin(lo,l,mid);
    if(mid<qr)b=querymin(ro,mid+1,r);
    return min(a,b);
}
int querymax(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return ma[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymax(lo,l,mid);
    if(mid<qr)b=querymax(ro,mid+1,r);
    return max(a,b);
}

void check()
{
//    cout<<endl;
//    cout<<mi[1]<<' ';
    qr=ql=n;
    if(mi[1]<0||querymin(1,1,n)!=0)
    {
        ans[cnt++]=-1;
        return;
    }
    ql=1;
    ans[cnt++]=querymax(1,1,n);
}

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d",&n);
    build(1,1,n);
    for(int i=0;i<n;i++)
    {
        char op;
        op=getchar();
        //cout<<op<<endl;
        if(op=='R')
        {
            st++;
            ans[cnt++]=ans[cnt-2];
        }
        else if(op=='L')
        {
            st--;
            ans[cnt++]=ans[cnt-2];
        }
        else
        {
            ql=st,qr=n;
            if(op=='(')
            {
                qv=1-arr[st];
                arr[st]=1;
            }
            else if(op==')')
            {
                qv=-1-arr[st];
                arr[st]=-1;
            }
            else
            {
                qv=-arr[st];
                arr[st]=0;
            }
            add(1,1,n);
            check();
        }
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d",ans[i]);
        if(i!=n)printf(" ");
    }

    return 0;
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const ll linf=0x3f3f3f3f3f3f3f3f;

int mi[maxn*4],ma[maxn<<2],lazy[maxn*4],arr[maxn],ans[maxn],cnt=1;
int qv;
int ql,qr;
int n,st=1;

void build(int o,int l,int r)
{
    if(l==r)
    {
        mi[o]=0;
        ma[o]=0;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    build(lo,l,mid);
    build(ro,mid+1,r);
}

void update(int o,int l,int r)
{
    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    mi[lo]+=lazy[o];
    ma[lo]+=lazy[o];
    mi[ro]+=lazy[o];
    ma[ro]+=lazy[o];
    lazy[lo]+=lazy[o];
    lazy[ro]+=lazy[o];
    lazy[o]=0;
}

void add(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        mi[o]+=qv;
        ma[o]+=qv;
        lazy[o]+=qv;
        return;
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    if(mid>=ql)add(lo,l,mid);
    if(mid<qr)add(ro,mid+1,r);
    mi[o]=min(mi[lo],mi[ro]);
    ma[o]=max(ma[lo],ma[ro]);
}

int querymin(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return mi[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymin(lo,l,mid);
    if(mid<qr)b=querymin(ro,mid+1,r);
    return min(a,b);
}
int querymax(int o,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return ma[o];
    }

    int mid=(l+r)>>1,lo=o<<1,ro=o<<1|1;
    update(o,l,r);

    int a,b;
    if(mid>=ql)a=querymax(lo,l,mid);
    if(mid<qr)b=querymax(ro,mid+1,r);
    return max(a,b);
}

void check()
{
//    cout<<endl;
//    cout<<mi[1]<<' ';
    qr=ql=n;
    if(mi[1]<0||querymin(1,1,n)!=0)
    {
        ans[cnt++]=-1;
        return;
    }
    ql=1;
    ans[cnt++]=querymax(1,1,n);
}

int main()
{
    //freopen("in.txt","r",stdin);

    scanf("%d",&n);
    build(1,1,n);
    for(int i=0;i<n;i++)
    {
        char op;
        op=getchar();
        cout<<op<<endl;
        //cout<<op<<endl;
        if(op=='R')
        {
            st++;
            ans[cnt++]=ans[cnt-2];
        }
        else if(op=='L')
        {
            st--;
            ans[cnt++]=ans[cnt-2];
        }
        else
        {
            ql=st,qr=n;
            if(op=='(')
            {
                qv=1-arr[st];
                arr[st]=1;
            }
            else if(op==')')
            {
                qv=-1-arr[st];
                arr[st]=-1;
            }
            else
            {
                qv=-arr[st];
                arr[st]=0;
            }
            add(1,1,n);
            check();
        }
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d",ans[i]);
        if(i!=n)printf(" ");
    }

    return 0;
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
const int B=500;
using namespace std;
using namespace __gnu_pbds;
#define be begin()
#define rb rbegin()
#define all(v) v.begin(),v.end()
#define rep(i,start,lim) for(long long (i)=(start);i<(lim);i++)
#define nod int node,int l,int r
#define lson node<<1,l,(l+r)/2
#define rson node<<1|1,(l+r)/2+1,r
typedef long long ll;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> rbTree;
const ll inf= 1e9+10;
ll MOD=  1e9+7 ;
const ll mxn= 1e6+10;
const int N=10000005;
const double pi=3.14159;
#define mp make_pair
#define mp3(a,b,c) mp(a,mp(b,c))
typedef pair<int,int> pii;
typedef pair<int,pair<int,int> > piii;
typedef pair<int,pair<int,pair<int,int>>> piiii;
typedef pair<ll, int> pli;
typedef pair<ll,ll> pll;
int dx[]={0,1,0,-1,1,-1,1,-1};
int dy[]={1,0,-1,0,-1,1,1,-1};
ll n,k,m,q,x,y;
int val[mxn], lazy[mxn<<2];
struct bracket{
    int minBal,mxBal;
    bracket(){ minBal=mxBal=0; }
    bracket(int bal,int pos){ minBal=bal, mxBal=pos; }
} T[mxn<<2];

void pu(int node){
    T[node<<1].minBal+=lazy[node], T[node<<1].mxBal+=lazy[node];
    T[node<<1|1].minBal+=lazy[node], T[node<<1|1].mxBal+=lazy[node];
    lazy[node<<1]+=lazy[node], lazy[node<<1|1]+=lazy[node];
    lazy[node]=0;
}

void merge(int node){
    T[node].minBal=min(T[node<<1].minBal,T[node<<1|1].minBal);
    T[node].mxBal=max(T[node<<1].mxBal,T[node<<1|1].mxBal);
    //T[node].sum=T[node<<1].sum+lazy[node<<1]+T[node<<1|1].sum+lazy[node<<1|1];
}

void upd(nod, int L, int R, int val){
    if(l>R||r<L||l>r) return;
    if(l>=L&&r<=R){
        //cout<<node<<' '<<l<<' '<<r<<' '<<val<<endl;
        T[node].minBal+=val, T[node].mxBal+=val;
        //cout<<node<<' '<<l<<' '<<r<<' '<<T[node].minBal<<' '<<T[node].mxBal<<endl;
        lazy[node]+=val;
        return;
    }
    pu(node);
    upd(lson,L,R,val), upd(rson,L,R,val);
    merge(node);
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin>>n;
    int cur=1, tot=0, mx=0, curMin=0;
    val[0]=0;
    for(int i=1;i<=n;i++){
        char k;
        cin>>k;
        cur+=(k=='R'?1:(k=='L'?-1:0));
        cur=max(cur,1);
        int add=0, ans=-1;
        if(k=='(') add=1;
        else if(k==')') add=-1;

        if(k!='L'&&k!='R') {
            if(add-val[cur]) upd(1,1,n,cur,n,add-val[cur]), tot+=(add-val[cur]);
            val[cur]=add;
        }

        //cout<<cur<<' '<<val[cur]<<endl;
        if(T[1].minBal<0||tot!=0) ans=-1;
        else ans=T[1].mxBal;
        //if(i==n) cout<<T[1].minBal<<' '<<tot<<endl;
        cout<<ans<<' ';
    }
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
const int B=500;
using namespace std;
using namespace __gnu_pbds;
#define be begin()
#define rb rbegin()
#define all(v) v.begin(),v.end()
#define rep(i,start,lim) for(long long (i)=(start);i<(lim);i++)
#define nod int node,int l,int r
#define lson node<<1,l,(l+r)/2
#define rson node<<1|1,(l+r)/2+1,r
typedef long long ll;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> rbTree;
const ll inf= 1e9+10;
ll MOD=  1e9+7 ;
const ll mxn= 1e6+10;
const int N=10000005;
const double pi=3.14159;
#define mp make_pair
#define mp3(a,b,c) mp(a,mp(b,c))
typedef pair<int,int> pii;
typedef pair<int,pair<int,int> > piii;
typedef pair<int,pair<int,pair<int,int>>> piiii;
typedef pair<ll, int> pli;
typedef pair<ll,ll> pll;
int dx[]={0,1,0,-1,1,-1,1,-1};
int dy[]={1,0,-1,0,-1,1,1,-1};
ll n,k,m,q,x,y;
struct bracket{
    int minBal, o, c, mxPos;
    bracket(){ minBal=o=c=mxPos=0; }
    bracket(int bal, int o, int c, int pos){ minBal=bal, this->o=o, this->c=c, mxPos=pos; }
} T[mxn<<2];

inline int max(int a,int b,int c) { return max(a,max(b,c)); }

void merge(int node){
    int lo,lc,lmin,ro,rc,rmin,lmx,rmx;
    tie(lmin,lo,lc,lmx)=make_tuple(T[node<<1].minBal, T[node<<1].o, T[node<<1].c,T[node<<1].mxPos);
    tie(rmin,ro,rc,rmx)=make_tuple(T[node<<1|1].minBal, T[node<<1|1].o, T[node<<1|1].c, T[node<<1|1].mxPos);
    int mn=min(lo,rc);
    lo-=mn,rc-=mn;
    T[node]=bracket(lmin+rc,lo+ro,lc+rc,max(lmx,rmx,lo+ro));
    //cout<<node<<' '<<T[node].o<<' '<<T[node].c<<endl;
}

void upd(nod, int L, int R, int val){
    if(l>R||r<L||l>r) return;
    if(l>=L&&r<=R){
        if(val==1)
            T[node]=bracket(0,1,0,1);
        else
            T[node]=bracket(1,0,1,0);
        return;
    }
    upd(lson,L,R,val), upd(rson,L,R,val);
    merge(node);
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin>>n;
    int cur=1;
    for(int i=1;i<=n;i++){
        char k;
        cin>>k;
        cur+=(k=='R'?1:(k=='L'?-1:0));
        if(k=='(') upd(1,1,n,cur,cur,1);
        else if(k==')') upd(1,1,n,cur,cur,-1);
        int ans=-1;
        if(T[1].minBal||T[1].o||T[1].c) ans=-1;
        else ans=T[1].mxPos;
        cout<<ans<<' ';
    }
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
const int B=500;
using namespace std;
using namespace __gnu_pbds;
#define be begin()
#define rb rbegin()
#define all(v) v.begin(),v.end()
#define rep(i,start,lim) for(long long (i)=(start);i<(lim);i++)
#define nod int node,int l,int r
#define lson node<<1,l,(l+r)/2
#define rson node<<1|1,(l+r)/2+1,r
typedef long long ll;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> rbTree;
const ll inf= 1e9+10;
ll MOD=  1e9+7 ;
const ll mxn= 1e6+10;
const int N=10000005;
const double pi=3.14159;
#define mp make_pair
#define mp3(a,b,c) mp(a,mp(b,c))
typedef pair<int,int> pii;
typedef pair<int,pair<int,int> > piii;
typedef pair<int,pair<int,pair<int,int>>> piiii;
typedef pair<ll, int> pli;
typedef pair<ll,ll> pll;
int dx[]={0,1,0,-1,1,-1,1,-1};
int dy[]={1,0,-1,0,-1,1,1,-1};
ll n,k,m,q,x,y;
struct bracket{
    int minBal, o, c, mxPos;
    bracket(){ minBal=o=c=mxPos=0; }
    bracket(int bal, int o, int c, int pos){ minBal=bal, this->o=o, this->c=c, mxPos=pos; }
} T[mxn<<2];

inline int max(int a,int b,int c) { return max(a,max(b,c)); }

void merge(int node){
    int lo,lc,lmin,ro,rc,rmin,lmx,rmx;
    tie(lmin,lo,lc,lmx)=make_tuple(T[node<<1].minBal, T[node<<1].o, T[node<<1].c,T[node<<1].mxPos);
    tie(rmin,ro,rc,rmx)=make_tuple(T[node<<1|1].minBal, T[node<<1|1].o, T[node<<1|1].c, T[node<<1|1].mxPos);
    int mn=min(lo,rc);
    lo-=mn,rc-=mn;
    T[node]=bracket(lmin+rc,lo+ro,lc+rc,max(lmx,rmx,lo+ro));
    //cout<<node<<' '<<T[node].o<<' '<<T[node].c<<endl;
}

void upd(nod, int L, int R, int val){
    if(l>R||r<L||l>r) return;
    if(l>=L&&r<=R){
        if(val==1)
            T[node]=bracket(0,1,0,1);
        else if(val==-1)
            T[node]=bracket(1,0,1,0);
        else
            T[node]=bracket(0,0,0,0);
        return;
    }
    upd(lson,L,R,val), upd(rson,L,R,val);
    merge(node);
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin>>n;
    int cur=1;
    for(int i=1;i<=n;i++){
        char k;
        cin>>k;
        cur+=(k=='R'?1:(k=='L'?-1:0));
        if(k=='(') upd(1,1,n,cur,cur,1);
        else if(k==')') upd(1,1,n,cur,cur,-1);
        else if(k!='L'&&k!='R') upd(1,1,n,cur,cur,0);
        int ans=-1;
        if(T[1].minBal||T[1].o||T[1].c) ans=-1;
        else ans=T[1].mxPos;
        cout<<ans<<' ';
    }
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
const int B=500;
using namespace std;
using namespace __gnu_pbds;
#define be begin()
#define rb rbegin()
#define all(v) v.begin(),v.end()
#define rep(i,start,lim) for(long long (i)=(start);i<(lim);i++)
#define nod int node,int l,int r
#define lson node*2,l,(l+r)/2
#define rson node*2+1,(l+r)/2+1,r
typedef long long ll;
typedef long double D;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> rbTree;
const ll inf= 1e9+12;
ll MOD=  1e9+7 ;
const ll mxn= 1e6+5;
const int N=10000005;
const double pi=3.14159;
#define mp make_pair
#define mp3(a,b,c) mp(c,mp(a,b))
typedef pair<int,int> pii;
typedef pair<int,pair<int,int> > piii;
typedef pair<ll, int> pli;
typedef pair<ll,ll> pll;
int dx[]={0,1,0,-1,1,-1,1,-1};
int dy[]={1,0,-1,0,-1,1,1,-1};
ll m,n,k,y,z;
char s[mxn], t[mxn];
int Lmin[mxn], balL[mxn], Lmax[mxn],  Rmin[mxn], balR[mxn], Rmax[mxn];
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin>>n;
    int h=1;
    for(int i=0;i<n;i++){
        cin>>s[i];
        if(s[i]=='L'){
            if(h>1) h--;
        }
        else if(s[i]=='R') h++;
        else t[h]=s[i];
        balL[h]=balL[h-1]+(t[h]=='('?1:(t[h]==')'?-1:0));
        Lmin[h]=min(Lmin[h-1], balL[h]);
        Lmax[h]=max(Lmax[h-1], balL[h]);
        balR[h]=balR[h+1]+(t[h]==')'?1:(t[h]=='('?-1:0));
        Rmin[h]=min(Rmin[h+1], balR[h]);
        Rmax[h]=max(Rmax[h+1], balR[h]);
        int ans=-1;
        //cout<<h<<' '<<balL[h]<<' '<<Lmin[h]<<' '<<Rmin[h+1]<<' '<<balR[h+1]<<' '<<t[h]<<endl;
        if(Lmin[h]>=0&&Rmin[h+1]>=0&&balL[h]==balR[h+1]){
            ans=max(Lmax[h], Rmax[h+1]);
        }
        cout<<ans<<' ';
    }
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
const int B=500;
using namespace std;
using namespace __gnu_pbds;
#define be begin()
#define rb rbegin()
#define all(v) v.begin(),v.end()
#define rep(i,start,lim) for(long long (i)=(start);i<(lim);i++)
#define nod int node,int l,int r
#define lson node<<1,l,(l+r)/2
#define rson node<<1|1,(l+r)/2+1,r
typedef long long ll;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> rbTree;
const ll inf= 1e9+10;
ll MOD=  1e9+7 ;
const ll mxn= 1e6+10;
const int N=10000005;
const double pi=3.14159;
#define mp make_pair
#define mp3(a,b,c) mp(a,mp(b,c))
typedef pair<int,int> pii;
typedef pair<int,pair<int,int> > piii;
typedef pair<int,pair<int,pair<int,int>>> piiii;
typedef pair<ll, int> pli;
typedef pair<ll,ll> pll;
int dx[]={0,1,0,-1,1,-1,1,-1};
int dy[]={1,0,-1,0,-1,1,1,-1};
ll n,k,m,q,x,y;
struct bracket{
    int minBal, o, c, mxPos;
    bracket(){ minBal=o=c=mxPos=0; }
    bracket(int bal, int o, int c, int pos){ minBal=bal, this->o=o, this->c=c, mxPos=pos; }
} T[mxn<<2];

inline int max(int a,int b,int c,int d) { return max(max(a,d),max(b,c)); }

void merge(int node){
    int lo,lc,lmin,ro,rc,rmin,lmx,rmx;
    tie(lmin,lo,lc,lmx)=make_tuple(T[node<<1].minBal, T[node<<1].o, T[node<<1].c,T[node<<1].mxPos);
    tie(rmin,ro,rc,rmx)=make_tuple(T[node<<1|1].minBal, T[node<<1|1].o, T[node<<1|1].c, T[node<<1|1].mxPos);
    int mn=min(lo,rc);
    lo-=mn,rc-=mn;
    T[node]=bracket(lmin+rc,lo+ro,lc+rc,max(lmx,rmx,lo+ro,lc+rc));
    //cout<<node<<' '<<T[node].o<<' '<<T[node].c<<endl;
}

void upd(nod, int L, int R, int val){
    if(l>R||r<L||l>r) return;
    if(l>=L&&r<=R){
        if(val==1)
            T[node]=bracket(0,1,0,1);
        else if(val==-1)
            T[node]=bracket(1,0,1,0);
        else
            T[node]=bracket(0,0,0,0);
        return;
    }
    upd(lson,L,R,val), upd(rson,L,R,val);
    merge(node);
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin>>n;
    int cur=1;
    for(int i=1;i<=n;i++){
        char k;
        cin>>k;
        cur+=(k=='R'?1:(k=='L'?-1:0));
        if(k=='(') upd(1,1,n,cur,cur,1);
        else if(k==')') upd(1,1,n,cur,cur,-1);
        else if(k!='L'&&k!='R') upd(1,1,n,cur,cur,0);
        int ans=-1;
        if(T[1].minBal||T[1].o||T[1].c) ans=-1;
        else ans=T[1].mxPos;
        cout<<ans<<' ';
    }
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
const int B=500;
using namespace std;
using namespace __gnu_pbds;
#define be begin()
#define rb rbegin()
#define all(v) v.begin(),v.end()
#define rep(i,start,lim) for(long long (i)=(start);i<(lim);i++)
#define nod int node,int l,int r
#define lson node<<1,l,(l+r)/2
#define rson node<<1|1,(l+r)/2+1,r
typedef long long ll;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> rbTree;
const ll inf= 1e9+10;
ll MOD=  1e9+7 ;
const ll mxn= 1e6+10;
const int N=10000005;
const double pi=3.14159;
#define mp make_pair
#define mp3(a,b,c) mp(a,mp(b,c))
typedef pair<int,int> pii;
typedef pair<int,pair<int,int> > piii;
typedef pair<int,pair<int,pair<int,int>>> piiii;
typedef pair<ll, int> pli;
typedef pair<ll,ll> pll;
int dx[]={0,1,0,-1,1,-1,1,-1};
int dy[]={1,0,-1,0,-1,1,1,-1};
ll n,k,m,q,x,y;
struct bracket{
    int minBal, o, c, mxPos;
    bracket(){ minBal=o=c=mxPos=0; }
    bracket(int bal, int o, int c, int pos){ minBal=bal, this->o=o, this->c=c, mxPos=pos; }
} T[mxn<<2];

inline int max(int a,int b,int c,int d) { return max(max(a,d),max(b,c)); }

void merge(int node){
    int lo,lc,lmin,ro,rc,rmin,lmx,rmx;
    tie(lmin,lo,lc,lmx)=make_tuple(T[node<<1].minBal, T[node<<1].o, T[node<<1].c,T[node<<1].mxPos);
    tie(rmin,ro,rc,rmx)=make_tuple(T[node<<1|1].minBal, T[node<<1|1].o, T[node<<1|1].c, T[node<<1|1].mxPos);
    int mn=min(lo,rc);
    lo-=mn,rc-=mn;
    T[node]=bracket(lmin+rc,lo+ro,lc+rc,max(lmx,rmx,lo+ro+mn,lc+rc+mn));
    //cout<<node<<' '<<T[node].o<<' '<<T[node].c<<endl;
}

void upd(nod, int L, int R, int val){
    if(l>R||r<L||l>r) return;
    if(l>=L&&r<=R){
        if(val==1)
            T[node]=bracket(0,1,0,1);
        else if(val==-1)
            T[node]=bracket(1,0,1,0);
        else
            T[node]=bracket(0,0,0,0);
        return;
    }
    upd(lson,L,R,val), upd(rson,L,R,val);
    merge(node);
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin>>n;
    int cur=1;
    for(int i=1;i<=n;i++){
        char k;
        cin>>k;
        cur+=(k=='R'?1:(k=='L'?-1:0));
        if(k=='(') upd(1,1,n,cur,cur,1);
        else if(k==')') upd(1,1,n,cur,cur,-1);
        else if(k!='L'&&k!='R') upd(1,1,n,cur,cur,0);
        int ans=-1;
        if(T[1].minBal||T[1].o||T[1].c) ans=-1;
        else ans=T[1].mxPos;
        cout<<ans<<' ';
    }
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
const int B=500;
using namespace std;
using namespace __gnu_pbds;
#define be begin()
#define rb rbegin()
#define all(v) v.begin(),v.end()
#define rep(i,start,lim) for(long long (i)=(start);i<(lim);i++)
#define nod int node,int l,int r
#define lson node<<1,l,(l+r)/2
#define rson node<<1|1,(l+r)/2+1,r
typedef long long ll;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> rbTree;
const ll inf= 1e9+10;
ll MOD=  1e9+7 ;
const ll mxn= 1e6+10;
const int N=10000005;
const double pi=3.14159;
#define mp make_pair
#define mp3(a,b,c) mp(a,mp(b,c))
typedef pair<int,int> pii;
typedef pair<int,pair<int,int> > piii;
typedef pair<int,pair<int,pair<int,int>>> piiii;
typedef pair<ll, int> pli;
typedef pair<ll,ll> pll;
int dx[]={0,1,0,-1,1,-1,1,-1};
int dy[]={1,0,-1,0,-1,1,1,-1};
ll n,k,m,q,x,y;
struct bracket{
    int minBal, o, c, mxPos;
    bracket(){ minBal=o=c=mxPos=0; }
    bracket(int bal, int o, int c, int pos){ minBal=bal, this->o=o, this->c=c, mxPos=pos; }
} T[mxn<<2];

inline int max(int a,int b,int c,int d) { return max(max(a,d),max(b,c)); }

void merge(int node){
    int lo,lc,lmin,ro,rc,rmin,lmx,rmx;
    tie(lmin,lo,lc,lmx)=make_tuple(T[node<<1].minBal, T[node<<1].o, T[node<<1].c,T[node<<1].mxPos);
    tie(rmin,ro,rc,rmx)=make_tuple(T[node<<1|1].minBal, T[node<<1|1].o, T[node<<1|1].c, T[node<<1|1].mxPos);
    int mn=min(lo,rc);
    lo-=mn,rc-=mn;
    T[node]=bracket(lmin+rc,lo+ro,lc+rc,max(lmx,rmx,lo+ro+mn*(lo>0),lc+rc+mn*(rc>0)));
    //cout<<node<<' '<<T[node].o<<' '<<T[node].c<<endl;
}

void upd(nod, int L, int R, int val){
    if(l>R||r<L||l>r) return;
    if(l>=L&&r<=R){
        if(val==1)
            T[node]=bracket(0,1,0,1);
        else if(val==-1)
            T[node]=bracket(1,0,1,0);
        else
            T[node]=bracket(0,0,0,0);
        return;
    }
    upd(lson,L,R,val), upd(rson,L,R,val);
    merge(node);
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin>>n;
    int cur=1;
    for(int i=1;i<=n;i++){
        char k;
        cin>>k;
        cur+=(k=='R'?1:(k=='L'?-1:0));
        cur=max(cur,1);
        if(k=='(') upd(1,1,n,cur,cur,1);
        else if(k==')') upd(1,1,n,cur,cur,-1);
        else if(k!='L'&&k!='R') upd(1,1,n,cur,cur,0);
        int ans=-1;
        if(T[1].minBal||T[1].o||T[1].c) ans=-1;
        else ans=T[1].mxPos;
        cout<<ans<<' ';
    }
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
const int B=500;
using namespace std;
using namespace __gnu_pbds;
#define be begin()
#define rb rbegin()
#define all(v) v.begin(),v.end()
#define rep(i,start,lim) for(long long (i)=(start);i<(lim);i++)
#define nod int node,int l,int r
#define lson node<<1,l,(l+r)/2
#define rson node<<1|1,(l+r)/2+1,r
typedef long long ll;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> rbTree;
const ll inf= 1e9+10;
ll MOD=  1e9+7 ;
const ll mxn= 1e6+10;
const int N=10000005;
const double pi=3.14159;
#define mp make_pair
#define mp3(a,b,c) mp(a,mp(b,c))
typedef pair<int,int> pii;
typedef pair<int,pair<int,int> > piii;
typedef pair<int,pair<int,pair<int,int>>> piiii;
typedef pair<ll, int> pli;
typedef pair<ll,ll> pll;
int dx[]={0,1,0,-1,1,-1,1,-1};
int dy[]={1,0,-1,0,-1,1,1,-1};
ll n,k,m,q,x,y;
int val[mxn], lazy[mxn<<2];
struct bracket{
    int minBal,mxBal;
    bracket(){ minBal=mxBal=0; }
    bracket(int bal,int pos){ minBal=bal, mxBal=pos; }
} T[mxn<<2];

void pu(int node){
    T[node<<1].minBal+=lazy[node], T[node<<1].mxBal+=lazy[node];
    T[node<<1|1].minBal+=lazy[node], T[node<<1|1].mxBal+=lazy[node];
    lazy[node<<1]+=lazy[node], lazy[node<<1|1]+=lazy[node];
    lazy[node]=0;
}

void merge(int node){
    T[node].minBal=min(T[node<<1].minBal,T[node<<1|1].minBal);
    T[node].mxBal=max(T[node<<1].mxBal,T[node<<1|1].mxBal);
    //T[node].sum=T[node<<1].sum+lazy[node<<1]+T[node<<1|1].sum+lazy[node<<1|1];
}

void upd(nod, int L, int R, int val){
    if(l>R||r<L||l>r) return;
    if(l>=L&&r<=R){
        //cout<<node<<' '<<l<<' '<<r<<' '<<val<<endl;
        T[node].minBal+=val, T[node].mxBal+=val;
        //cout<<node<<' '<<l<<' '<<r<<' '<<T[node].minBal<<' '<<T[node].mxBal<<endl;
        lazy[node]+=val;
        return;
    }
    pu(node);
    upd(lson,L,R,val), upd(rson,L,R,val);
    merge(node);
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin>>n;
    int cur=1, tot=0, mx=0, curMin=0;
    val[0]=0;
    for(int i=1;i<=n;i++){
        char k;
        cin>>k;
        cur+=(k=='R'?1:(k=='L'?-1:0));
        cur=max(cur,1);
        int add=0, ans=-1;
        if(k=='(') add=1;
        else if(k==')') add=-1;

        if(k!='L'&&k!='R') {
            if(add-val[cur]) upd(1,1,n,cur,n,add-val[cur]), tot+=(add-val[cur]);
        }
        val[cur]=add;

        if(T[1].minBal<0||tot!=0) ans=-1;
        else ans=T[1].mxBal;
        //cout<<T[1].minBal<<' '<<tot<<endl;
        cout<<ans<<' ';
    }
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
const int B=500;
using namespace std;
using namespace __gnu_pbds;
#define be begin()
#define rb rbegin()
#define all(v) v.begin(),v.end()
#define rep(i,start,lim) for(long long (i)=(start);i<(lim);i++)
#define nod int node,int l,int r
#define lson node<<1,l,(l+r)/2
#define rson node<<1|1,(l+r)/2+1,r
typedef long long ll;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> rbTree;
const ll inf= 1e9+10;
ll MOD=  1e9+7 ;
const ll mxn= 1e6+10;
const int N=10000005;
const double pi=3.14159;
#define mp make_pair
#define mp3(a,b,c) mp(a,mp(b,c))
typedef pair<int,int> pii;
typedef pair<int,pair<int,int> > piii;
typedef pair<int,pair<int,pair<int,int>>> piiii;
typedef pair<ll, int> pli;
typedef pair<ll,ll> pll;
int dx[]={0,1,0,-1,1,-1,1,-1};
int dy[]={1,0,-1,0,-1,1,1,-1};
ll n,k,m,q,x,y;
struct bracket{
    int minBal, o, c, mxPos;
    bracket(){ minBal=o=c=mxPos=0; }
    bracket(int bal, int o, int c, int pos){ minBal=bal, this->o=o, this->c=c, mxPos=pos; }
} T[mxn<<2];

inline int max(int a,int b,int c) { return max(a,max(b,c)); }

void merge(int node){
    int lo,lc,lmin,ro,rc,rmin,lmx,rmx;
    tie(lmin,lo,lc,lmx)=make_tuple(T[node<<1].minBal, T[node<<1].o, T[node<<1].c,T[node<<1].mxPos);
    tie(rmin,ro,rc,rmx)=make_tuple(T[node<<1|1].minBal, T[node<<1|1].o, T[node<<1|1].c, T[node<<1|1].mxPos);
    int mn=min(lo,rc);
    lo-=mn,rc-=mn;
    T[node]=bracket(lmin+rc,lo+ro,lc+rc,max(lmx,rmx,lo+ro+mn*(lo>0)));
    //cout<<node<<' '<<T[node].o<<' '<<T[node].c<<endl;
}

void upd(nod, int L, int R, int val){
    if(l>R||r<L||l>r) return;
    if(l>=L&&r<=R){
        if(val==1)
            T[node]=bracket(0,1,0,1);
        else if(val==-1)
            T[node]=bracket(1,0,1,0);
        else
            T[node]=bracket(0,0,0,0);
        return;
    }
    upd(lson,L,R,val), upd(rson,L,R,val);
    merge(node);
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin>>n;
    int cur=1;
    for(int i=1;i<=n;i++){
        char k;
        cin>>k;
        cur+=(k=='R'?1:(k=='L'?-1:0));
        cur=max(cur,1);
        if(k=='(') upd(1,1,n,cur,cur,1);
        else if(k==')') upd(1,1,n,cur,cur,-1);
        else if(k!='L'&&k!='R') upd(1,1,n,cur,cur,0);
        int ans=-1;
        if(T[1].minBal||T[1].o||T[1].c) ans=-1;
        else ans=T[1].mxPos;
        cout<<ans<<' ';
    }
}


#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#include<set>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int maxn = 1e6+2;
struct node{
	int l,r;
	int Min,Max;
	int val;
	int lazy;
}tree[maxn*4];
void pushdown(int index){
    if(tree[index].lazy){
        tree[index<<1].val += (tree[index<<1].r-tree[index<<1].l+1)*tree[index].lazy;
        tree[index<<1|1].val +=(tree[index<<1|1].r-tree[index<<1|1].l+1)*tree[index].lazy;
        tree[index<<1].Max += tree[index].lazy;
        tree[index<<1|1].Max += tree[index].lazy;
        tree[index<<1].Min += tree[index].lazy;
        tree[index<<1|1].Min += tree[index].lazy;
        tree[index<<1].lazy += tree[index].lazy;
        tree[index<<1|1].lazy += tree[index].lazy;
        tree[index].lazy = 0;
    }
}
void pushup(int index){
    tree[index].val = tree[index<<1].val+tree[index<<1|1].val;
    tree[index].Max = max(tree[index<<1].Max,tree[index<<1|1].Max);
    tree[index].Min = min(tree[index<<1].Min,tree[index<<1|1].Min);
}

void build(int l,int r,int index){//建树
	tree[index].l = l,tree[index].r = r,tree[index].lazy = 0;
	if(l == r){
        //scanf("%d",&tree[index].val);
        tree[index].val = 0;
        tree[index].Max = tree[index].Min = tree[index].val;
        return ;
	}
	int mid = (l+r)/2;//分段
    build(l,mid,index<<1);
    build(mid+1,r,index<<1|1);
    pushup(index);
}
void update(int l,int r,int index,int val){//区间修改
	if(l <= tree[index].l && r >= tree[index].r){
        /*把原来的值替换成val,因为该区间有tree[index].r-tree[index].l+1
        个数，所以区间和 以及 最值为：
        */
        /*tree[index].sum = (tree[index].r-tree[index].l+1)*val;
        tree[index].mn = val;
        tree[index].mx = val;
        tree[index].add = val;//延时标记*/
        //在原来的值的基础上加上val,因为该区间有tree[index].r-tree[index].l+1
        //个数，所以区间和 以及 最值为：
        tree[index].val += (tree[index].r-tree[index].l+1)*val;
        tree[index].Max += val;
        tree[index].Min += val;
        tree[index].lazy += val;//延时标记
        return ;
	}
	pushdown(index);
    int mid = (tree[index].l+tree[index].r)>>1;
    if(l <= mid){
        update(l,r,index<<1,val);
    }
    if(r > mid){
        update(l,r,index<<1|1,val);
    }
    pushup(index);
}
LL query_range(int l,int r,int index){//区间查询
	if(l <= tree[index].l && r >= tree[index].r){
        return tree[index].val;
        //return tree[index].mn;
    }
    pushdown(index);
    int mid = (tree[index].l+tree[index].r)>>1;
    LL res = 0;
    LL Max = 0;
    LL Min = inf;
    if(l <= mid){
        res += query_range(l,r,index<<1);
    }
    if(r > mid){
        res += query_range(l,r,index<<1|1);
    }
    return res;
}

LL query_max(int l,int r,int index){//最大值查询
	if(l<= tree[index].l && r>= tree[index].r ){
		return tree[index].Max;
	}
	pushdown(index);
	int mid = (tree[index].l + tree[index].r )>>1;
	LL ans = 0;
	LL MAX = 0,MIN = inf;
	if(l <= mid) MAX = max(query_max(l,r,index<<1),MAX);
	if(r > mid) MAX = max(query_max(l,r,index<<1|1),MAX);
	return MAX;
}
LL query_min(int l,int r,int index){//最小值查询
	if(l<= tree[index].l && r>= tree[index].r ){
		return tree[index].Min;
	}
	pushdown(index);
	int mid = (tree[index].l + tree[index].r )>>1;
	LL ans = 0;
	LL MAX = 0,MIN = inf;
	if(l <= mid) MIN = min(query_min(l,r,index<<1),MIN);
	if(r > mid) MIN = min(query_min(l,r,index<<1|1),MIN);
	return MIN;
}
string s;
int main(){
	//ios::sync_with_stdio(false);
	int n;
	scanf("%d",&n);
	build(1,n+2,1);
	cin>>s;
	LL ss[n+1];
	memset(ss,0,sizeof(ss));
	int pos = 1;
	vector<int> ans;
	for(int i = 0;i<s.length() ;i++){
		if(s[i] == '('){
			update(pos,n,1,1-ss[pos]);
			ss[pos] = 1;
		}
		else if(s[i] == ')'){
			update(pos,n,1,-1-ss[pos]);
			ss[pos] = -1;
		}
		else if(s[i] == 'R'){
			pos++;
		}
		else if(s[i] == 'L'){
			if(pos>1) pos--;
		}
		else{
			if(ss[pos] == 1){
				update(pos,n,1,-1);
			}
			else if(ss[pos] == -1){
				update(pos,n,1,1);
			}
			ss[pos] = 0;
		}
		if(query_min(1,n,1) == 0 && query_range(n,n,1) == 0){
			ans.push_back(query_max(1,n,1));
		}
		else{
			ans.push_back(-1);
		}
	}
	for(int i = 0;i<ans.size() ;i++){
		cout<<ans[i]<<" ";
	}
	return 0;
}