#include <queue>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <complex>
#include <fstream>
#include <cstring>
#include <string>
#include <climits>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;

//macros
typedef long long ll;
typedef complex<double> point;
typedef pair<int,int> ii;
typedef vector<int> vi;
typedef vector< vector<int> > vvi;

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

#define FOR(k,a,b) for(int k=(a); k<=(b); ++k)
#define REP(k,a) for(int k=0; k<(a);++k)
#define SZ(a) int((a).size())
#define ALL(c) (c).begin(),(c).end()
#define PB push_back
#define MP make_pair
#define INF 1999999999
#define INFLONG 1000000000000000000
#define MOD 1000000007
#define MAX 100
#define ITERS 100
#define MAXM 200000
#define MAXN 200000
#define _gcd __gcd
#define eps 1e-9
#define PI 3.1415926535897932384626

struct query{int ty; int ti; int val;
    query(int a, int b, int c){
        ty = a;
        ti = b;
        val = c;
    }
};

ordered_set addo[100000];
ordered_set remo[100000];

bool uso[100000];
int main() {
    ios_base::sync_with_stdio(false);
    vector<query> qs;
    vi timos;
    int n;
    cin >> n;
    REP(i,n){
        int a,b,c;
        cin >> a >> b >> c;
        query qo(a,b,c);
        qs.PB(qo);
        timos.PB(b);
    }
    sort(ALL(timos));
    map<int,int> mapo;
    REP(i,SZ(timos)){
        mapo[timos[i]] = i;
    }
    vector<query> queries;
    REP(i,SZ(qs)){
        qs[i].ti = mapo[qs[i].ti];
    }

    int ptr = 0;
    map<int,int> valo;
    map<int,bool> seen;
    REP(i,SZ(qs)){
        int vo = qs[i].val;
        if(!seen[vo]){
            seen[vo] = true;
            valo[vo] = ptr;
            ptr++;
        }
        qs[i].val = valo[vo];
    }
    /*
    REP(i,SZ(qs)){
        if(qs[i].ty==1){
            addsz[qs[i].val]++;
        }
        else if(qs[i].ty==2){
            remsz[qs[i].val]++;
        }
    }
    bool viso[100000];
    REP(i,100000) viso[i] = false;
    */
    REP(i,SZ(qs)){
        int ty = qs[i].ty;
        int ti = qs[i].ti;
        int vo = qs[i].val;
        if(ty==1){
            addo[vo].insert(ti);
        }
        else if(ty==2){
            remo[vo].insert(ti);
            uso[vo] = true;
        }
        else if(ty==3){
            int ans = 0;
            if(uso[vo]){
                ans += addo[vo].order_of_key(ti);
                ans -= remo[vo].order_of_key(ti);
            }
            else{
                ans = addo[vo].order_of_key(ti);
            }
            cout << ans << endl;
        }
    }
}


#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
const int M = 40000005;
int n;
unordered_map<int , int> cnt;
struct opt {
	int t , x , s;
};
stack< opt > S;

int main() {
	scanf("%d" , &n);
	for (int i = 0 ; i < n ; ++ i) {
		int o , t , x;
		scanf("%d%d%d" , &o , &t , &x);
		//printf("-%d %d %d\n" , o , t , x);
		while (!S.empty() && S.top().t > t) {
			opt w = S.top() ; S.pop();
			cnt[w.x] -= w.s;
		}
		if (o < 3) {
			opt w;
			if (o == 1) {
				w.t = t;
				w.x = x;
				w.s = 1;
			} else {
				w.t = t;
				w.x = x;
				w.s = -1;
			}
			cnt[x] += w.s;
			S.push(w);
		} else {
			printf("%d\n" , cnt[x]);
		}
	}
}


#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
const int M = 40000005;
int n;
unordered_map<int , int> c[N];
struct opt {
	int o , t , x;
}q[N];
int d[N] , D;
int main() {
	scanf("%d" , &n);
	for (int i = 0 ; i < n ; ++ i) {
		int o , t , x;
		scanf("%d%d%d" , &o , &t , &x);
		q[i] = (opt) {o , t , x};
		d[D ++] = t;
	}
	sort(d , d + D);
	D = unique(d , d + D) - d;
	for (int i = 0 ; i < n ; ++ i) {
		int j = lower_bound(d , d + D , q[i].t) - d + 1;
		if (q[i].o < 3) {
			int w = q[i].o == 1 ? 1 : -1;
			for (int k = j ; k <= D ; k += k & -k) {
				c[k][q[i].x] += w;
			}
		} else {
			int res = 0;
			for (int k = j ; k > 0 ; k -= k & -k) {
				if (c[k].count(q[i].x)) {
					res += c[k][q[i].x];
				}
			}
			printf("%d\n" , res);
		}

	}
}


#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
const int M = 40000005;
int n;
unordered_map<int , int> cnt;
struct opt {
	int t , x , s;
};
stack< opt > S;

int main() {
	scanf("%d" , &n);
	for (int i = 0 ; i < n ; ++ i) {
		int o , t , x;
		scanf("%d%d%d" , &o , &t , &x);
		//printf("-%d %d %d\n" , o , t , x);
		while (!S.empty() && S.top().t > t) {
			opt w = S.top() ; S.pop();
			cnt[w.x] -= w.s;
		}
		if (o < 3) {
			opt w;
			if (o == 1) {
				w.t = t;
				w.x = x;
				w.s = 1;
				++ cnt[x];
			} else {
				w.t = t;
				w.x = x;
				if (cnt[x]) {
					w.s = -1;
					-- cnt[x];
				} else {
					w.s = 0;
				}
			}
			S.push(w);
		} else {
			printf("%d\n" , cnt[x]);
		}
	}
}


#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <string.h>
#include <string>
#include <limits.h>
#include <algorithm>
#include <set>
#include <ctime>
#include <cmath>
using namespace std;
#define SZ(x) ((int)(x).size())
#define rep(i,a,n) for (int i=a; i<(int)n; i++)
#define per(i,n,a) for (int i=n; i>=a; i--)
#define hk push_back
#define pk pop_back
#define mp make_pair
#define PI 3.141592653589793
#define clr(a) memset(a, 0, sizeof(a))
#define clr1(a) memset(a, -1, sizeof(a))
#define endl "\n"
#define c_false ios_base::sync_with_stdio(false); cin.tie(0)
typedef vector<int> VI;
typedef vector< pair<int, int> > VIP;
typedef vector< pair<int, pair<int, double> > > VIPP;
typedef vector<string> VS;
typedef vector <double> VD;
typedef vector <bool> VB;
typedef long long ll;
#define MAX_V 1000
const ll mod = 1000000007;
ll powmod(ll a, ll b) {
	ll res = 1; a %= mod; for (; b; b >>= 1){ if (b & 1)res = res*a%mod; a = a*a%mod; }return res;
}

int n, a, t, x;
map< int, set<int> > dat;

int main()
{
	c_false;
	cin >> n;
	rep(i, 0, n) {
		cin >> a >> t >> x;
		if (a == 1) {
			if (dat.count(x)) dat[x].insert(t);
			else {
				set<int> s;
				s.insert(x);
				dat[x] = s;
			}
		}
		else if (a == 2) {
			dat[x].erase(x);
		}
		else {
			if (dat.count(x)) {
				auto it = dat[x].upper_bound(x);
				cout << distance(dat[x].begin(), it) << endl;
			}
			else cout << 0 << endl;
		}
	}

	return 0;
}


#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <string.h>
#include <string>
#include <limits.h>
#include <algorithm>
#include <set>
#include <ctime>
#include <cmath>
using namespace std;
#define SZ(x) ((int)(x).size())
#define rep(i,a,n) for (int i=a; i<(int)n; i++)
#define per(i,n,a) for (int i=n; i>=a; i--)
#define hk push_back
#define pk pop_back
#define mp make_pair
#define PI 3.141592653589793
#define clr(a) memset(a, 0, sizeof(a))
#define clr1(a) memset(a, -1, sizeof(a))
#define endl "\n"
#define c_false ios_base::sync_with_stdio(false); cin.tie(0)
typedef vector<int> VI;
typedef vector< pair<int, int> > VIP;
typedef vector< pair<int, pair<int, double> > > VIPP;
typedef vector<string> VS;
typedef vector <double> VD;
typedef vector <bool> VB;
typedef long long ll;
#define MAX_V 1000
const ll mod = 1000000007;
ll powmod(ll a, ll b) {
	ll res = 1; a %= mod; for (; b; b >>= 1){ if (b & 1)res = res*a%mod; a = a*a%mod; }return res;
}

int n, a, t, x;
map< int, set<int> > added, removed;

int main()
{
//	c_false;
//	cin >> n;
	scanf("%d", &n);
	rep(i, 0, n) {
//		cin >> a >> t >> x;
		scanf("%d%d%d", &a, &t, &x);
		if (a == 1) {
			if (added.count(x)) added[x].insert(t);
			else {
				set<int> s;
				s.insert(t);
				added[x] = s;
			}
		}
		else if (a == 2) {
			if (removed.count(x)) removed[x].insert(t);
			else {
				set<int> s;
				s.insert(t);
				removed[x] = s;
			}
		}
		else {
			int aCnt = 0, rCnt = 0;
			if (added.count(x)) {
				auto it = added[x].upper_bound(t);
				if (it != added[x].end() && *it == t) it++;
				aCnt = distance(added[x].begin(), it);
			}
			if (removed.count(x)) {
				auto it = removed[x].upper_bound(t);
				if (it != removed[x].end() && *it == t) it++;
				rCnt = distance(removed[x].begin(), it);
			}
			printf("%d\n", aCnt - rCnt);
//			cout << aCnt - rCnt << endl;
		}
	}

	return 0;
}


#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <string.h>
#include <string>
#include <limits.h>
#include <algorithm>
#include <set>
#include <ctime>
#include <cmath>
using namespace std;
#define SZ(x) ((int)(x).size())
#define rep(i,a,n) for (int i=a; i<(int)n; i++)
#define per(i,n,a) for (int i=n; i>=a; i--)
#define hk push_back
#define pk pop_back
#define mp make_pair
#define PI 3.141592653589793
#define clr(a) memset(a, 0, sizeof(a))
#define clr1(a) memset(a, -1, sizeof(a))
#define endl "\n"
#define c_false ios_base::sync_with_stdio(false); cin.tie(0)
typedef vector<int> VI;
typedef vector< pair<int, int> > VIP;
typedef vector< pair<int, pair<int, double> > > VIPP;
typedef vector<string> VS;
typedef vector <double> VD;
typedef vector <bool> VB;
typedef long long ll;
#define MAX_V 1000
const ll mod = 1000000007;
ll powmod(ll a, ll b) {
	ll res = 1; a %= mod; for (; b; b >>= 1){ if (b & 1)res = res*a%mod; a = a*a%mod; }return res;
}

int n, a, t, x;
map< int, set<int> > added, removed;

int main()
{
	c_false;
	cin >> n;
	rep(i, 0, n) {
		cin >> a >> t >> x;
		if (a == 1) {
			if (added.count(x)) added[x].insert(t);
			else {
				set<int> s;
				s.insert(t);
				added[x] = s;
			}
		}
		else if (a == 2) {
			if (removed.count(x)) removed[x].insert(t);
			else {
				set<int> s;
				s.insert(t);
				removed[x] = s;
			}
		}
		else {
			int aCnt = 0, rCnt = 0;
			if (added.count(x)) {
				auto it = added[x].upper_bound(t);
				if (it != added[x].end() && *it == t) it++;
				aCnt = distance(added[x].begin(), it);
			}
			if (removed.count(x)) {
				auto it = removed[x].upper_bound(t);
				if (it != removed[x].end() && *it == t) it++;
				rCnt = distance(removed[x].begin(), it);
			}

			cout << aCnt - rCnt << endl;
		}
	}

	return 0;
}


#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <string.h>
#include <string>
#include <limits.h>
#include <algorithm>
#include <set>
#include <ctime>
#include <cmath>
using namespace std;
#define SZ(x) ((int)(x).size())
#define rep(i,a,n) for (int i=a; i<(int)n; i++)
#define per(i,n,a) for (int i=n; i>=a; i--)
#define hk push_back
#define pk pop_back
#define mp make_pair
#define PI 3.141592653589793
#define clr(a) memset(a, 0, sizeof(a))
#define clr1(a) memset(a, -1, sizeof(a))
#define endl "\n"
#define c_false ios_base::sync_with_stdio(false); cin.tie(0)
typedef vector<int> VI;
typedef vector< pair<int, int> > VIP;
typedef vector< pair<int, pair<int, double> > > VIPP;
typedef vector<string> VS;
typedef vector <double> VD;
typedef vector <bool> VB;
typedef long long ll;
#define MAX_V 1000
const ll mod = 1000000007;
ll powmod(ll a, ll b) {
	ll res = 1; a %= mod; for (; b; b >>= 1){ if (b & 1)res = res*a%mod; a = a*a%mod; }return res;
}

int n, a, t, x;
map< int, map<int, int> > added, removed;

int main()
{
//	c_false;
//	cin >> n;
	scanf("%d", &n);
	rep(i, 0, n) {
//		cin >> a >> t >> x;
		scanf("%d%d%d", &a, &t, &x);
		if (a == 1) {
			for (; !(t >> 31); t += (t & -t)) added[x][t]++;
		}
		else if (a == 2) {
			for (; !(t >> 31); t += (t & -t)) removed[x][t]++;
		}
		else {
			int aCnt = 0, rCnt = 0, t1 = t;
			for (; t; t -= (t & -t)) aCnt += added[x][t];
			t = t1;
			for (; t; t -= (t & -t)) rCnt += removed[x][t];
			printf("%d\n", aCnt - rCnt);
//			cout << aCnt - rCnt << endl;
		}
	}

	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//hello//hello


/*
 * File: stuff.cpp
 * Author: Andy Y.F. Huang (azneye)
 */

#include <bits/stdc++.h>

using namespace std;

namespace stuff {
typedef long long ll;
const int MAX = 1 << 17;
map<int, int> bit[MAX];
int V;

void update(int pos, int val, int tim) {
  for (int i = pos; i <= V; i += i & -i) {
    auto it = bit[i].lower_bound(tim);
    --it;
    bit[i][tim] = it->second + val;
  }
}

int get(int pos, int tim) {
  int res = 0;
  for (int i = pos; i > 0; i -= i & -i) {
    auto it = bit[i].lower_bound(tim);
    --it;
    res += it->second;
  }
  return res;
}

void solve(ll test_num) {
  (void) test_num;
  int N;
  scanf("%d", &N);
  vector<int> ty(N), tim(N), x(N);
  for (int i = 0; i < N; ++i) {
    scanf("%d %d %d", &ty[i], &tim[i], &x[i]);
  }
  vector<int> vals = x;
  sort(vals.begin(), vals.end());
  vals.erase(unique(vals.begin(), vals.end()), vals.end());
  V = (int) vals.size();
  for (int v = 1; v <= V; ++v) {
    bit[v][0] = 0;
  }
  for (int i = 0; i < N; ++i) {
    const int pos = (int) (lower_bound(vals.begin(), vals.end(), x[i]) - vals.begin()) + 1;
    if (ty[i] == 1) {
      update(pos, 1, tim[i]);
    } else if (ty[i] == 2) {
      update(pos, -1, tim[i]);
    } else {
      const int res = get(pos, tim[i]);
      printf("%d\n", res);
    }
  }
}

void solve() {
#ifdef AZN
//make_case();
  double start_t = (double) clock();
  freopen("input.txt", "r", stdin);
  freopen("output.txt", "w", stdout);
//freopen("azn.txt", "w", stderr  );
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  ll T = 1;
//  cin >> T;
  for (ll t = 1; t <= T; t++)
    solve(t);
#ifdef AZN
  cerr << fixed << setprecision(3) << "Took: " << (((double) clock() - start_t) / CLOCKS_PER_SEC)
      << endl;
#endif
}
}

int main() {
  stuff::solve();
  return 0;
}


/*
 * File: stuff.cpp
 * Author: Andy Y.F. Huang (azneye)
 */

#include <bits/stdc++.h>

using namespace std;

namespace stuff {
typedef long long ll;
const int MAX = 1 << 17;
map<int, int> bit[MAX];
int V;

void update(int pos, int val, int tim) {
  for (int i = pos; i <= V; i += i & -i) {
    auto it = bit[i].lower_bound(tim);
    --it;
    bit[i][tim] = it->second + val;
  }
}

int get(int pos, int tim) {
  int res = 0;
  for (int i = pos; i > 0; i -= i & -i) {
    auto it = bit[i].lower_bound(tim);
    --it;
    res += it->second;
  }
  for (int i = pos - 1; i > 0; i -= i & -i) {
    auto it = bit[i].lower_bound(tim);
    --it;
    res -= it->second;
  }
  return res;
}

void solve(ll test_num) {
  (void) test_num;
  int N;
  scanf("%d", &N);
  vector<int> ty(N), tim(N), x(N);
  for (int i = 0; i < N; ++i) {
    scanf("%d %d %d", &ty[i], &tim[i], &x[i]);
  }
  vector<int> vals = x;
  sort(vals.begin(), vals.end());
  vals.erase(unique(vals.begin(), vals.end()), vals.end());
  V = (int) vals.size();
  for (int v = 1; v <= V; ++v) {
    bit[v][0] = 0;
  }
  for (int i = 0; i < N; ++i) {
    const int pos = (int) (lower_bound(vals.begin(), vals.end(), x[i]) - vals.begin()) + 1;
    if (ty[i] == 1) {
      update(pos, 1, tim[i]);
    } else if (ty[i] == 2) {
      update(pos, -1, tim[i]);
    } else {
      const int res = get(pos, tim[i]);
      printf("%d\n", res);
    }
  }
}

void solve() {
#ifdef AZN
//make_case();
  double start_t = (double) clock();
  freopen("input.txt", "r", stdin);
  freopen("output.txt", "w", stdout);
//freopen("azn.txt", "w", stderr  );
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  ll T = 1;
//  cin >> T;
  for (ll t = 1; t <= T; t++)
    solve(t);
#ifdef AZN
  cerr << fixed << setprecision(3) << "Took: " << (((double) clock() - start_t) / CLOCKS_PER_SEC)
      << endl;
#endif
}
}

int main() {
  stuff::solve();
  return 0;
}


/*
 * File: stuff.cpp
 * Author: Andy Y.F. Huang (azneye)
 */

#include <bits/stdc++.h>

using namespace std;

namespace stuff {
typedef long long ll;
const int MAX = 1 << 17;
map<int, int> bit[MAX];
int V, T;

void update(int pos, int tim, int val) {
  for (int x = tim; x <= T; x += x & -x) {
    bit[x][pos] += val;
  }
}

int get(int pos, int tim) {
  int res = 0;
  for (int x = tim; x > 0; x -= x & -x) {
    res += bit[x][pos];
  }
  return res;
}

void solve(ll test_num) {
  (void) test_num;
  int N;
  scanf("%d", &N);
  vector<int> ty(N), tim(N), x(N);
  for (int i = 0; i < N; ++i) {
    scanf("%d %d %d", &ty[i], &tim[i], &x[i]);
  }
  vector<int> times = tim, vals = x;
  sort(times.begin(), times.end());
  sort(vals.begin(), vals.end());
  vals.erase(unique(vals.begin(), vals.end()), vals.end());
  V = (int) vals.size();
  T = (int) times.size();
  for (int v = 1; v <= V; ++v) {
    bit[v][0] = 0;
  }
  for (int i = 0; i < N; ++i) {
    const int pos = (int) (lower_bound(vals.begin(), vals.end(), x[i]) - vals.begin()) + 1;
    const int t = (int) (lower_bound(times.begin(), times.end(), tim[i]) - times.begin()) + 1;
    if (ty[i] == 1) {
      update(pos, t, 1);
    } else if (ty[i] == 2) {
      update(pos, t, -1);
    } else {
      const int res = get(pos, t);
      printf("%d\n", res);
    }
  }
}

void solve() {
#ifdef AZN
//make_case();
  double start_t = (double) clock();
  freopen("input.txt", "r", stdin);
  freopen("output.txt", "w", stdout);
//freopen("azn.txt", "w", stderr  );
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  ll T = 1;
//  cin >> T;
  for (ll t = 1; t <= T; t++)
    solve(t);
#ifdef AZN
  cerr << fixed << setprecision(3) << "Took: " << (((double) clock() - start_t) / CLOCKS_PER_SEC)
      << endl;
#endif
}
}

int main() {
  stuff::solve();
  return 0;
}


#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
struct query
{
	int tp, t, x, idx;
	inline bool operator <(const query &a) const
	{
		if (x == a.x)
			return idx < a.idx;
		return x < a.x;
	}
} arr[100005];
int sum[100005], ans[100005], n, app[100005];
bool vis[100005];
inline void modify(int pos, int x)
{
	for (; pos <= n; pos += pos & -pos)
		sum[pos] += x;
}
inline int query(int pos)
{
	int res = 0;
	for (; pos; pos -= pos & -pos)
		res += sum[pos];
	return res;
}
int main()
{
	// freopen("668D.in", "r", stdin);
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d%d%d", &arr[i].tp, &arr[i].t, &arr[i].x);
		app[i] = arr[i].t;
		if (arr[i].tp == 3)
			vis[i] = true;
		arr[i].idx = i;
	}
	sort(app, app + n);
	sort(arr, arr + n);
	for (int i = 0; i < n; i++)
		arr[i].t = lower_bound(app, app + n, arr[i].t) - app + 1;
	for (int i = 0, idx = 0; i < n; i = idx)
	{
		while (idx < n && arr[idx].x == arr[i].x)
			idx++;
		for (int j = i; j < idx; j++)
		{
			if (arr[j].tp == 1)
				modify(arr[j].t, 1);
			else if (arr[j].tp == 2)
				modify(arr[j].t, -1);
			else
				ans[arr[j].idx] = query(arr[j].t);
		}
		for (int j = i; j < idx; j++)
		{
			if (arr[j].tp == 1)
				modify(arr[j].t, -1);
			else if (arr[j].tp == 2)
				modify(arr[j].t, 1);
		}
	}
	for (int i = 0; i < n; i++)
	{
		if (vis[i])
			printf("%d\n", ans[i]);
	}
	return 0;
}


#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
struct query_tp
{
	int tp, t, x, idx;
	inline bool operator <(const query_tp &a) const
	{
		if (x == a.x)
			return idx < a.idx;
		return x < a.x;
	}
} arr[100005];
int app[100005], sum[100005], ans[100005], q[100005];
int main()
{
	// freopen("668D.in", "r", stdin);
	int n, cnt = 0;
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d%d%d", &arr[i].tp, &arr[i].t, &arr[i].x);
		if (arr[i].tp == 3)
			q[cnt++] = i;
		arr[i].idx = i;
		app[i] = arr[i].t;
	}
	sort(app, app + n);
	for (int i = 0; i < n; i++)
		arr[i].t = lower_bound(app, app + n, arr[i].t) - app + 1;
	sort(arr, arr + n);
	for (int i = 0, j = 0; i < n; i++, i = j)
	{
		while (j < n && arr[j].x == arr[i].x)
			j++;
		for (int k = i; k < j; k++)
		{
			if (arr[k].tp == 1)
			{
				for (int x = arr[k].t; x <= n; x += x & -x)
					sum[x]++;
			}
			else if (arr[k].tp == 2)
			{
				for (int x = arr[k].t; x <= n; x += x & -x)
					sum[x]--;
			}
			else
			{
				for (int x = arr[k].t; x; x -= x & -x)
					ans[arr[k].idx] += sum[x];
			}
		}
		for (int k = i; k < j; k++)
		{
			if (arr[k].tp == 1)
			{
				for (int x = arr[k].t; x <= n; x += x & -x)
					sum[x]--;
			}
			else if (arr[k].tp == 2)
			{
				for (int x = arr[k].t; x <= n; x += x & -x)
					sum[x]++;
			}
		}
	}
	for (int i = 0; i < cnt; i++)
		printf("%d\n", ans[q[i]]);
	return 0;
}


#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;

const int MAXN = 1<<17;

int Q;
map<int, int> t[2*MAXN];

int query(int i, int x, int n = 1, int lo = 0, int hi = MAXN-1) {
	if(lo > i)
		return 0;
	if(hi <= i)
		return t[n].count(x)? t[n][x] : 0;
	return query(i, x, 2*n, lo, (lo+hi)/2) + query(i, x, 2*n+1, (lo+hi)/2+1, hi);
}

int main() {
	//freopen("A.in", "r", stdin);
	scanf("%d", &Q);
	for(int i = 1, a, q, x; i <= Q; i++) {
		scanf("%d%d%d", &a, &q, &x);
		if(a <= 2) {
			for(int n = MAXN+q; n >= 1; n /= 2)
				t[n][x] += a == 1 ? 1 : -1;
		} else {
			printf("%d\n", query(q, x));
		}
	}
}


#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;

const int MAXN = 1e5 + 10;

int Q;
map<int, int> t[2*MAXN];
vector<int> val;

int main() {
	//freopen("A.in", "r", stdin);
	scanf("%d", &Q);
	for(int i = 1, a, q, x; i <= Q; i++) {
		scanf("%d%d%d", &a, &q, &x);
		val.push_back(q);
	}
	sort(val.begin(), val.end());
	val.resize(unique(val.begin(), val.end()) - val.begin());
	rewind(stdin);
	for(int i = 1, a, q, x; i <= Q; i++) {
		scanf("%d%d%d", &a, &q, &x);
		q = lower_bound(val.begin(), val.end(), q) - val.begin() + 1;
		if(a <= 2) {
			for(int n = q; n < MAXN; n += n&-n)
				t[n][x] += a == 1 ? 1 : -1;
		} else {
			int ans = 0;
			for(int n = q; n >= 1; n -= n&-n)
				ans += t[n][x];
		}
	}
}


#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;

const int MAXN = 1<<17;

int Q;
map<int, int> t[2*MAXN];

int query(int i, int x, int n = 1, int lo = 0, int hi = MAXN-1) {
	if(lo > i)
		return 0;
	if(hi <= i)
		return t[n].count(x)? 0 : t[n][x];
	return query(i, x, 2*n, lo, (lo+hi)/2) + query(i, x, 2*n+1, (lo+hi)/2+1, hi);
}

int main() {
	//freopen("A.in", "r", stdin);
	scanf("%d", &Q);
	for(int i = 1, a, q, x; i <= Q; i++) {
		scanf("%d%d%d", &a, &q, &x);
		if(a <= 2) {
			for(int n = MAXN+q; n >= 1; n /= 2)
				t[n][x] += a == 1 ? 1 : -1;
		} else {
			printf("%d\n", query(q, x));
		}
	}
}


#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;

const int MAXN = 1<<17;

int Q;
map<int, int> t[2*MAXN];

int query(int i, int x, int n = 1, int lo = 0, int hi = MAXN-1) {
	if(lo > i)
		return 0;
	if(hi <= i)
		return t[n][x];
	return query(i, x, 2*n, lo, (lo+hi)/2) + query(i, x, 2*n+1, (lo+hi)/2+1, hi);
}

int main() {
	//freopen("A.in", "r", stdin);
	scanf("%d", &Q);
	for(int i = 1, a, q, x; i <= Q; i++) {
		scanf("%d%d%d", &a, &q, &x);
		if(a <= 2) {
			for(int n = MAXN+q; n >= 1; n /= 2)
				t[n][x] += a == 1 ? 1 : -1;
		} else {
			printf("%d\n", query(q, x));
		}
	}
}


#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;

const int MAXN = 1e5 + 10;

int Q;
map<int, int> t[2*MAXN];
vector<int> val;

int main() {
	//freopen("A.in", "r", stdin);
	scanf("%d", &Q);
	for(int i = 1, a, q, x; i <= Q; i++) {
		scanf("%d%d%d", &a, &q, &x);
		val.push_back(q);
	}
	sort(val.begin(), val.end());
	val.resize(unique(val.begin(), val.end()) - val.begin());
	rewind(stdin);
	scanf("%d", &Q);
	for(int i = 1, a, q, x; i <= Q; i++) {
		scanf("%d%d%d", &a, &q, &x);
		q = lower_bound(val.begin(), val.end(), q) - val.begin() + 1;
		if(a <= 2) {
			for(int n = q; n < MAXN; n += n&-n)
				t[n][x] += a == 1 ? 1 : -1;
		} else {
			int ans = 0;
			for(int n = q; n >= 1; n -= n&-n)
				ans += t[n][x];
			printf("%d\n", ans);
		}
	}
}


//In the name of Allah
#include <bits/stdc++.h>
using namespace std;

const int maxN = 1000 * 1000 + 10;

int fen[maxN];
inline void add( int x , int v ) {
	x++;
	for( ; x < maxN ; x += x & (-x) )
		fen[x] += v;
}
inline int get( int x ) {
	int ret = 0;
	for( ; x ; x -= x & -x )
		ret += fen[x];
	return ret;
}

int t[maxN];
int que[maxN];

int ans[maxN];

pair<int,int> a[maxN];
set<int> curr;

int main() {
	ios::sync_with_stdio(false); cin.tie(0);
	int n; cin >> n;
	for( int i = 0 ; i < n ; i++ ) {
		int tp , x , time; cin >> tp >> time >> x;
		a[i] = { x , i } ;
		t[i] = tp ; que[i] = time;
	}
	sort( a , a + n ) ;
	int j;
	for( int i = 0 ; i < n ; i = j ) {
		for( j = i ; j < n && a[i].first == a[j].first ; j++ ) ;
		for( int k = i ; k < j ; k++ ) {
			int ind = a[k].second;
			if( t[ind] == 1 ) {
				add( que[ind] , 1 ) ;
			} else if( t[ind] == 2 ) {
				add( que[ind] , -1 ) ;
			} else
				ans[ind] = get( que[ind] ) ;
		}
		for( int k = i ; k < j ; k++ ) {
			int ind = a[k].second;
			if( t[ind] == 1 )
				add( que[ind] , -1 ) ;
			else if( t[ind] == 2 )
				add( que[ind] , 1 ) ;
		}
	}
	for( int i = 0 ; i < n; i++ )
		if( t[i] == 3 )
			cout << ans[i] << '\n';
}


//In the name of Allah
#include <bits/stdc++.h>
using namespace std;

const int maxN = 1000 * 1000 + 10;

int fen[maxN];
inline void add( int x , int v ) {
	x++;
	for( ; x < maxN ; x += x & (-x) )
		fen[x] += v;
}
inline int get( int x ) {
	int ret = 0;
	for( ; x ; x -= x & -x )
		ret += fen[x];
	return ret;
}

int t[maxN];
int que[maxN];

int ans[maxN];

pair<int,int> a[maxN];
set<int> curr;

vector<int> X;
int main() {
	ios::sync_with_stdio(false); cin.tie(0);
	int n; cin >> n;
	for( int i = 0 ; i < n ; i++ ) {
		int tp , x , time; cin >> tp >> time >> x;
		a[i] = { x , i } ;
		t[i] = tp ; que[i] = time;
		X.push_back(time);
	}
	sort( X.begin() , X.end() ) ;
	for( int i = 0 ; i < n ; i++ )
		que[i] = lower_bound(X.begin(),X.end(),que[i]) - X.begin() ;
	sort( a , a + n ) ;
	int j;
	for( int i = 0 ; i < n ; i = j ) {
		for( j = i ; j < n && a[i].first == a[j].first ; j++ ) ;
		for( int k = i ; k < j ; k++ ) {
			int ind = a[k].second;
			if( t[ind] == 1 ) {
				add( que[ind] , 1 ) ;
			} else if( t[ind] == 2 ) {
				add( que[ind] , -1 ) ;
			} else
				ans[ind] = get( que[ind] ) ;
		}
		for( int k = i ; k < j ; k++ ) {
			int ind = a[k].second;
			if( t[ind] == 1 )
				add( que[ind] , -1 ) ;
			else if( t[ind] == 2 )
				add( que[ind] , 1 ) ;
		}
	}
	for( int i = 0 ; i < n; i++ )
		if( t[i] == 3 )
			cout << ans[i] << '\n';
}


//In the name of Allah
#include <bits/stdc++.h>
using namespace std;

const int maxN = 1000 * 1000 + 10;

int fen[maxN];
inline void add( int x , int v ) {
	x++;
	for( ; x < maxN ; x += x & (-x) )
		fen[x] += v;
}
inline int get( int x ) {
	int ret = 0;
	for( ; x ; x -= x & -x )
		ret += fen[x];
	return ret;
}

int t[maxN];
int que[maxN];

int ans[maxN];

pair<int,int> a[maxN];
set<int> curr;

int main() {
	ios::sync_with_stdio(false); cin.tie(0);
	int n; cin >> n;
	for( int i = 0 ; i < n ; i++ ) {
		int tp , x , time; cin >> tp >> time >> x;
		a[i] = { x , i } ;
		t[i] = tp ; que[i] = time;
	}
	sort( a , a + n ) ;
	int j;
	for( int i = 0 ; i < n ; i = j ) {
		for( j = i ; j < n && a[i].first == a[j].first ; j++ ) ;
		for( int k = i ; k < j ; k++ ) {
			int ind = a[k].second;
			if( t[ind] == 1 ) {
				curr.insert( que[ind] ) ;
				add( que[ind] , 1 ) ;
			} else if( t[ind] == 2 ) {
				int x = *curr.lower_bound( que[ind] ) ;
				curr.erase( x ) ;
				add( x , -1 ) ;
			} else
				ans[ind] = get( que[ind] ) ;
		}
		for( auto x : curr )
			add( x , -1 ) ;
		curr.clear();
	}
	for( int i = 0 ; i < n; i++ )
		if( t[i] == 3 )
			cout << ans[i] << '\n';
}


//In the name of Allah
#include <bits/stdc++.h>
using namespace std;

const int maxN = 1000 * 1000 + 10;

int fen[maxN];
inline void add( int x , int v ) {
	x++;
	for( ; x < maxN ; x += x & (-x) )
		fen[x] += v;
}
inline int get( int x ) {
	int ret = 0;
	for( ; x ; x -= x & -x )
		ret += fen[x];
	return ret;
}

int t[maxN];
int que[maxN];

int ans[maxN];

pair<int,int> a[maxN];
set<int> curr;

int main() {
	ios::sync_with_stdio(false); cin.tie(0);
	int n; cin >> n;
	for( int i = 0 ; i < n ; i++ ) {
		int tp , x , time; cin >> tp >> time >> x;
		a[i] = { x , i } ;
		t[i] = tp ; que[i] = time;
	}
	sort( a , a + n ) ;
	int j;
	for( int i = 0 ; i < n ; i = j ) {
		for( j = i ; j < n && a[i].first == a[j].first ; j++ ) ;
		for( int k = i ; k < j ; k++ ) {
			int ind = a[k].second;
			if( t[ind] == 1 ) {
				add( que[ind] , 1 ) ;
			} else if( t[ind] == 2 ) {
				add( que[ind] , -1 ) ;
			} else
				ans[ind] = get( que[ind] ) ;
		}
		curr.clear();
	}
	for( int i = 0 ; i < n; i++ )
		if( t[i] == 3 )
			cout << ans[i] << '\n';
}


//In the name of Allah
#include <bits/stdc++.h>
using namespace std;

const int maxN = 1000 * 1000 + 10;

int fen[maxN];
inline void add( int x , int v ) {
	x++;
	for( ; x < maxN ; x += x & (-x) )
		fen[x] += v;
}
inline int get( int x ) {
	int ret = 0;
	for( ; x ; x -= x & -x )
		ret += fen[x];
	return ret;
}

int t[maxN];
int que[maxN];

int ans[maxN];

pair<int,int> a[maxN];
set<int> curr;

int main() {
	ios::sync_with_stdio(false); cin.tie(0);
	int n; cin >> n;
	for( int i = 0 ; i < n ; i++ ) {
		int tp , x , time; cin >> tp >> time >> x;
		a[i] = { x , i } ;
		t[i] = tp ; que[i] = time;
	}
	sort( a , a + n ) ;
	int j;
	for( int i = 0 ; i < n ; i = j ) {
		for( j = i ; j < n && a[i].first == a[j].first ; j++ ) ;
		for( int k = i ; k < j ; k++ ) {
			int ind = a[k].second;
			if( t[ind] == 1 ) {
				add( que[ind] , 1 ) ;
			} else if( t[ind] == 2 ) {
				add( que[ind] , -1 ) ;
			} else
				ans[ind] = get( que[ind] ) ;
		}
		for( int k = i ; k < j ; k++ ) {
			int ind = a[k].second;
			if( t[ind] == 1 )
				add( que[ind] , -1 ) ;
			} else if( t[ind] == 2 )
				add( que[ind] , 1 ) ;
		}
	}
	for( int i = 0 ; i < n; i++ )
		if( t[i] == 3 )
			cout << ans[i] << '\n';
}


#include<bits/stdc++.h>

#define PB push_back
#define SZ(c) int((c).size())
#define CONTAINS(c, x) ((c).find((x)) != (c).end())
#define REP(i, c) for(int i=0; i<c; i++)
#define PER(i, c) for(int i=c-1; i>=0; i--)
#ifdef ONLINE_JUDGE
   #define WAIT
#else
   #define WAIT cout<<flush, system("PAUSE")
#endif

using namespace std;
typedef long long ll;
const int MAX = 100100;

int Q, op, x, y;
map<int, int> M[MAX];

void update(int x, int y, int val){
	while(x < MAX){
		M[x][y] += val;
		x += (x&-x);
	}
}
int query(int x, int y){
	int ret = 0;
	while(x > 0){
		ret += M[x][y];
		x -= (x&-x);
	}
	return ret;
}

int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(0);

	cin >> Q;
	REP(q, Q){
		cin>> op >> x >> y;
		if (op==1) update(x,y, +1);
		if (op==2) update(x,y, -1);
		if (op==3) cout << query(x,y) <<'\n';
	}
}


#include<bits/stdc++.h>

#define PB push_back
#define SZ(c) int((c).size())
#define CONTAINS(c, x) ((c).find((x)) != (c).end())
#define REP(i, c) for(int i=0; i<c; i++)
#define PER(i, c) for(int i=c-1; i>=0; i--)
#ifdef ONLINE_JUDGE
   #define WAIT
#else
   #define WAIT cout<<flush, system("PAUSE")
#endif

using namespace std;
typedef long long ll;

int Q, op, x, y;
map<int, map<int, int>> M;

void update(int x, int y, int val){
	if (x==0) return;

	while(x < 1e9+10){
		M[x][y] += val;
		x += (x&-x);
	}
}
int query(int x, int y){
	int ret = 0;
	while(x > 0){
		ret += M[x][y];
		x -= (x&-x);
	}
	return ret;
}

int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(0);

	cin >> Q;
	REP(q, Q){
		cin>> op >> x >> y;
		if (op==1) update(x,y, +1);
		if (op==2) update(x,y, -1);
		if (op==3) cout << query(x,y) <<'\n';
	}
}


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,tot,rt[100010],sz,h[100010],opt[100010],p[100010],v[100010];
struct Segment_tree
{
	int ls,rs,v;
}t[10000010];
inline void modify(int &x,int l,int r,int p,int v)
{
	if(!x)x=++sz;
	if(l==r)
	{
		t[x].v+=v;
		return ;
	}
	int mid=(l+r)>>1;
	if(p<=mid)modify(t[x].ls,l,mid,p,v);
	else modify(t[x].rs,mid+1,r,p,v);
}
inline int query(int x,int l,int r,int p)
{
	if(l==r)return t[x].v;
	int mid=(l+r)>>1;
	if(p<=mid)return query(t[x].ls,l,mid,p);
	else return query(t[x].rs,mid+1,r,p);
}
inline void modify(int x,int p,int v)
{
	for(int i=x;i<=m;i+=i&-i)modify(rt[i],1,tot,p,v);
}
inline int query(int x,int p)
{
	int res=0;
	for(int i=x;i;i-=i&-i)res+=query(rt[i],1,tot,p);
	return res;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&opt[i],&p[i],&v[i]);
		h[i]=p[i];
	}
	sort(h+1,h+n+1);
	m=unique(h+1,h+n+1)-h-1;
	for(int i=1;i<=n;i++)p[i]=lower_bound(h+1,h+m+1,p[i])-h;
	memset(h,0,sizeof(h));
	for(int i=1;i<=n;i++)h[i]=v[i];
	sort(h+1,h+n+1);
	tot=unique(h+1,h+n+1)-h-1;
	for(int i=1;i<=n;i++)v[i]=lower_bound(h+1,h+tot+1,v[i])-h;
	for(int i=1;i<=n;i++)
	{
		if(opt[i]==1)modify(p[i],v[i],1);
		else if(opt[i]==2)modify(p[i],v[i],-1);
		else printf("%d\n",query(p[i],v[i]));
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,tot,rt[100010],sz,h[100010],opt[100010],p[100010],v[100010];
struct Segment_tree
{
	int ls,rs,v;
}t[10000010];
inline void modify(int &x,int l,int r,int p,int v)
{
	if(l==r)
	{
		if(!x)x=++sz;
		t[x].v+=v;
		return ;
	}
	int mid=(l+r)>>1;
	if(p<=mid)modify(t[x].ls,l,mid,p,v);
	else modify(t[x].rs,mid+1,r,p,v);
}
inline int query(int x,int l,int r,int p)
{
	if(l==r)return t[x].v;
	int mid=(l+r)>>1;
	if(p<=mid)return query(t[x].ls,l,mid,p);
	else return query(t[x].rs,mid+1,r,p);
}
inline void modify(int x,int p,int v)
{
	for(int i=x;i<=m;i+=i&-i)modify(rt[i],1,tot,p,v);
}
inline int query(int x,int p)
{
	int res=0;
	for(int i=x;i;i-=i&-i)res+=query(rt[i],1,tot,p);
	return res;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&opt[i],&p[i],&v[i]);
		h[i]=p[i];
	}
	sort(h+1,h+n+1);
	m=unique(h+1,h+n+1)-h-1;
	for(int i=1;i<=n;i++)p[i]=lower_bound(h+1,h+m+1,p[i])-h;
	for(int i=1;i<=n;i++)h[i]=v[i];
	tot=unique(h+1,h+n+1)-h-1;
	for(int i=1;i<=n;i++)v[i]=lower_bound(h+1,h+m+1,v[i])-h;
	for(int i=1;i<=n;i++)
	{
		if(opt[i]==1)modify(p[i],v[i],1);
		else if(opt[i]==2)modify(p[i],v[i],-1);
		else printf("%d\n",query(p[i],v[i]));
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,tot,rt[100010],sz,h[100010],opt[100010],p[100010],v[100010];
struct Segment_tree
{
	int ls,rs,v;
}t[10000010];
inline void modify(int &x,int l,int r,int p,int v)
{
	if(l==r)
	{
		if(!x)x=++sz;
		t[x].v+=v;
		return ;
	}
	int mid=(l+r)>>1;
	if(p<=mid)modify(t[x].ls,l,mid,p,v);
	else modify(t[x].rs,mid+1,r,p,v);
}
inline int query(int x,int l,int r,int p)
{
	if(l==r)return t[x].v;
	int mid=(l+r)>>1;
	if(p<=mid)return query(t[x].ls,l,mid,p);
	else return query(t[x].rs,mid+1,r,p);
}
inline void modify(int x,int p,int v)
{
	for(int i=x;i<=m;i+=i&-i)modify(rt[i],1,tot,p,v);
}
inline int query(int x,int p)
{
	int res=0;
	for(int i=x;i;i-=i&-i)res+=query(rt[i],1,tot,p);
	return res;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&opt[i],&p[i],&v[i]);
		h[i]=p[i];
	}
	sort(h+1,h+n+1);
	m=unique(h+1,h+n+1)-h-1;
	for(int i=1;i<=n;i++)p[i]=lower_bound(h+1,h+m+1,p[i])-h;
	memset(h,0,sizeof(h));
	for(int i=1;i<=n;i++)h[i]=v[i];
	tot=unique(h+1,h+n+1)-h-1;
	for(int i=1;i<=n;i++)v[i]=lower_bound(h+1,h+tot+1,v[i])-h;
	for(int i=1;i<=n;i++)
	{
		if(opt[i]==1)modify(p[i],v[i],1);
		else if(opt[i]==2)modify(p[i],v[i],-1);
		else printf("%d\n",query(p[i],v[i]));
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,tot,rt[100010],sz,h[100010],opt[100010],p[100010],v[100010];
struct Segment_tree
{
	int ls,rs,v;
}t[10000010];
inline void modify(int &x,int l,int r,int p,int v)
{
	if(l==r)
	{
		if(!x)x=++sz;
		t[x].v+=v;
		return ;
	}
	int mid=(l+r)>>1;
	if(p<=mid)modify(t[x].ls,l,mid,p,v);
	else modify(t[x].rs,mid+1,r,p,v);
}
inline int query(int x,int l,int r,int p)
{
	if(l==r)return t[x].v;
	int mid=(l+r)>>1;
	if(p<=mid)return query(t[x].ls,l,mid,p);
	else return query(t[x].rs,mid+1,r,p);
}
inline void modify(int x,int p,int v)
{
	for(int i=x;i<=m;i+=i&-i)modify(rt[i],1,tot,p,v);
}
inline int query(int x,int p)
{
	int res=0;
	for(int i=x;i;i-=i&-i)res+=query(rt[i],1,tot,p);
	return res;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&opt[i],&p[i],&v[i]);
		h[i]=p[i];
	}
	sort(h+1,h+n+1);
	m=unique(h+1,h+n+1)-h-1;
	for(int i=1;i<=n;i++)p[i]=lower_bound(h+1,h+m+1,p[i])-h;
	memset(h,0,sizeof(h));
	for(int i=1;i<=n;i++)h[i]=v[i];
	sort(h+1,h+n+1);
	tot=unique(h+1,h+n+1)-h-1;
	for(int i=1;i<=n;i++)v[i]=lower_bound(h+1,h+tot+1,v[i])-h;
	for(int i=1;i<=n;i++)
	{
		if(opt[i]==1)modify(p[i],v[i],1);
		else if(opt[i]==2)modify(p[i],v[i],-1);
		else printf("%d\n",query(p[i],v[i]));
	}
	return 0;
}


#include<bits/stdc++.h>
#define ALL(X)        X.begin(),X.end()
#define FOR(I,A,B)    for(int (I) = (A); (I) <= (B); (I)++)
#define FORW(I,A,B)   for(int (I) = (A); (I) < (B);  (I)++)
#define FORD(I,A,B)   for(int (I) = (A); (I) >= (B); (I)--)
#define CLEAR(X)      memset(X,0,sizeof(X))
#define SIZE(X)       int(X.size())
#define CONTAINS(A,X) (A.find(X) != A.end())
#define PB            push_back
#define MP            make_pair
#define X             first
#define Y             second
using namespace std;
template<typename T, typename U> ostream& operator << (ostream& os, const pair<T, U> &_p) { return os << "(" << _p.X << "," << _p.Y << ")"; }
template<typename T> ostream& operator << (ostream &os, const vector<T>& _V) { bool f = true; os << "["; for(auto v: _V) { os << (f ? "" : ",") << v; f = false; } return os << "]"; }
template<typename T> ostream& operator << (ostream &os, const set<T>& _S) { bool f = true; os << "("; for(auto s: _S) { os << (f ? "" : ",") << s; f = false; } return os << ")"; }
template<typename T, typename U> ostream& operator << (ostream &os, const map<T, U>& _M) { return os << set<pair<T, U>>(ALL(_M)); }
typedef signed long long slong;
typedef long double ldouble;
const slong INF = 1000000100;
const ldouble EPS = 1e-9;

#define A aoisd
#define M aosijdo
struct Fenwick {
    int *A, M;

    Fenwick(int n) {
        M = 1<<(32-__builtin_clz(n));
        A = new int[M];
        FORW(i,0,M) A[i] = 0;
    }

    int query(int k) {
        int r = 0;
        for(; k > 0; k -= (k&-k)) r += A[k];
        return r;
    }

    int query(int a, int b) {
        if(b == 0) return query(a);
        else return query(b) - query(a-1);
    }

    void insert(int k, int v) {
        for(; k < M; k += (k&-k)) A[k] += v;
    }

    ~Fenwick() {
        delete[] A;
    }
};

int N;
const int MAXN = 100010;
vector<tuple<int, int, int>> raw;
vector<tuple<int, int, int>> queries[MAXN];
int values = 0;

void read_data() {
    scanf("%d", &N);
    map<int, int> M;
    FOR(i,1,N) {
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        raw.emplace_back(a, b, c);
        if(!CONTAINS(M, c)) M[c] = ++values;
    }
    int id = 0;
    for(auto row: raw) {
        ++id;
        int type, time, value;
        tie(type, time, value) = row;
        queries[M[value]].emplace_back(type, time, id);
    }
    FOR(i,1,values) {
        M.clear();
        for(auto row: queries[i]) {
            M[get<1>(row)] = 0;
        }
        int cnt = 0;
        for(auto &m: M) m.Y = ++cnt;
        for(auto &row: queries[i]) {
            int a, b, c;
            tie(a, b, c) = row;
            row = make_tuple(a, M[b], c);
        }
    }
}

map<int, int> R;

void solve() {
    FOR(v,1,values) {
        Fenwick T(SIZE(queries[values]) + 100);
        for(auto q: queries[v]) {
            int type, time, id;
            tie(type, time, id) = q;
            if(type == 1) {
                T.insert(time, 1);
            } else if(type == 2) {
                T.insert(time, -1);
            } else {
                R[id] = T.query(time);
//                cout << id << " " << T.query(time) << endl;
            }
        }
    }
    for(auto r: R) printf("%d\n", r.Y);
}

int main() {
    read_data();
    solve();
}


#include<bits/stdc++.h>
#define ALL(X)        X.begin(),X.end()
#define FOR(I,A,B)    for(int (I) = (A); (I) <= (B); (I)++)
#define FORW(I,A,B)   for(int (I) = (A); (I) < (B);  (I)++)
#define FORD(I,A,B)   for(int (I) = (A); (I) >= (B); (I)--)
#define CLEAR(X)      memset(X,0,sizeof(X))
#define SIZE(X)       int(X.size())
#define CONTAINS(A,X) (A.find(X) != A.end())
#define PB            push_back
#define MP            make_pair
#define X             first
#define Y             second
using namespace std;
template<typename T, typename U> ostream& operator << (ostream& os, const pair<T, U> &_p) { return os << "(" << _p.X << "," << _p.Y << ")"; }
template<typename T> ostream& operator << (ostream &os, const vector<T>& _V) { bool f = true; os << "["; for(auto v: _V) { os << (f ? "" : ",") << v; f = false; } return os << "]"; }
template<typename T> ostream& operator << (ostream &os, const set<T>& _S) { bool f = true; os << "("; for(auto s: _S) { os << (f ? "" : ",") << s; f = false; } return os << ")"; }
template<typename T, typename U> ostream& operator << (ostream &os, const map<T, U>& _M) { return os << set<pair<T, U>>(ALL(_M)); }
typedef signed long long slong;
typedef long double ldouble;
const slong INF = 1000000100;
const ldouble EPS = 1e-9;

#define A aoisd
#define M aosijdo
struct Fenwick {
    int *A, M;

    Fenwick(int n) {
        M = 1<<(32-__builtin_clz(n));
        A = new int[M];
        FORW(i,0,M) A[i] = 0;
    }

    int query(int k) {
        int r = 0;
        for(; k > 0; k -= (k&-k)) r += A[k];
        return r;
    }

    int query(int a, int b) {
        if(b == 0) return query(a);
        else return query(b) - query(a-1);
    }

    void insert(int k, int v) {
        for(; k < M; k += (k&-k)) A[k] += v;
    }

    ~Fenwick() {
        delete[] A;
    }
};

int N;
const int MAXN = 100010;
vector<tuple<int, int, int>> raw;
vector<tuple<int, int, int>> queries[MAXN];
int values = 0;

void read_data() {
    scanf("%d", &N);
    map<int, int> M;
    FOR(i,1,N) {
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        raw.emplace_back(a, b, c);
        if(!CONTAINS(M, c)) M[c] = ++values;
    }
    int id = 0;
    for(auto row: raw) {
        ++id;
        int type, time, value;
        tie(type, time, value) = row;
        queries[M[value]].emplace_back(type, time, id);
    }
    FOR(i,1,values) {
        M.clear();
        for(auto row: queries[i]) {
            M[get<1>(row)] = 0;
        }
        int cnt = 0;
        for(auto &m: M) m.Y = ++cnt;
        for(auto &row: queries[i]) {
            int a, b, c;
            tie(a, b, c) = row;
            row = make_tuple(a, M[b], c);
        }
    }
}

map<int, int> R;

void solve() {
    FOR(v,1,values) {
        Fenwick T(SIZE(queries[values]) + 10);
        for(auto q: queries[v]) {
            int type, time, id;
            tie(type, time, id) = q;
            if(type == 1) {
                T.insert(time, 1);
            } else if(type == 2) {
                T.insert(time, -1);
            } else {
                R[id] = T.query(time);
//                cout << id << " " << T.query(time) << endl;
            }
        }
    }
    for(auto r: R) printf("%d\n", r.Y);
}

int main() {
    read_data();
    solve();
}


#include<bits/stdc++.h>
#define ALL(X)        X.begin(),X.end()
#define FOR(I,A,B)    for(int (I) = (A); (I) <= (B); (I)++)
#define FORW(I,A,B)   for(int (I) = (A); (I) < (B);  (I)++)
#define FORD(I,A,B)   for(int (I) = (A); (I) >= (B); (I)--)
#define CLEAR(X)      memset(X,0,sizeof(X))
#define SIZE(X)       int(X.size())
#define CONTAINS(A,X) (A.find(X) != A.end())
#define PB            push_back
#define MP            make_pair
#define X             first
#define Y             second
using namespace std;
template<typename T, typename U> ostream& operator << (ostream& os, const pair<T, U> &_p) { return os << "(" << _p.X << "," << _p.Y << ")"; }
template<typename T> ostream& operator << (ostream &os, const vector<T>& _V) { bool f = true; os << "["; for(auto v: _V) { os << (f ? "" : ",") << v; f = false; } return os << "]"; }
template<typename T> ostream& operator << (ostream &os, const set<T>& _S) { bool f = true; os << "("; for(auto s: _S) { os << (f ? "" : ",") << s; f = false; } return os << ")"; }
template<typename T, typename U> ostream& operator << (ostream &os, const map<T, U>& _M) { return os << set<pair<T, U>>(ALL(_M)); }
typedef signed long long slong;
typedef long double ldouble;
const slong INF = 1000000100;
const ldouble EPS = 1e-9;

#define A aoisd
#define M aosijdo
struct Fenwick {
    int *A, M;

    Fenwick(int n) {
        M = 1<<(32-__builtin_clz(n));
        A = new int[M];
        FORW(i,0,M) A[i] = 0;
    }

    int query(int k) {
        int r = 0;
        for(; k > 0; k -= (k&-k)) r += A[k];
        return r;
    }

    int query(int a, int b) {
        if(b == 0) return query(a);
        else return query(b) - query(a-1);
    }

    void insert(int k, int v) {
        for(; k < M; k += (k&-k)) A[k] += v;
    }

    ~Fenwick() {
        delete[] A;
    }
};

int N;
const int MAXN = 100010;
vector<tuple<int, int, int>> raw;
vector<tuple<int, int, int>> queries[MAXN];
int values = 0;

void read_data() {
    scanf("%d", &N);
    map<int, int> M;
    FOR(i,1,N) {
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        raw.emplace_back(a, b, c);
        if(!CONTAINS(M, c)) M[c] = ++values;
    }
    int id = 0;
    for(auto row: raw) {
        ++id;
        int type, time, value;
        tie(type, time, value) = row;
        queries[M[value]].emplace_back(type, time, id);
    }
    FOR(i,1,values) {
        M.clear();
        for(auto row: queries[i]) {
            M[get<1>(row)] = 0;
        }
        int cnt = 0;
        for(auto &m: M) m.Y = ++cnt;
        for(auto &row: queries[i]) {
            int a, b, c;
            tie(a, b, c) = row;
            row = make_tuple(a, M[b], c);
        }
    }
}

map<int, int> R;

void solve() {
    FOR(v,1,values) {
        Fenwick T(SIZE(queries[v]) + 1000);
        for(auto q: queries[v]) {
            int type, time, id;
            tie(type, time, id) = q;
            if(type == 1) {
                T.insert(time, 1);
            } else if(type == 2) {
                T.insert(time, -1);
            } else {
                R[id] = T.query(time);
//                cout << id << " " << T.query(time) << endl;
            }
        }
    }
    for(auto r: R) printf("%d\n", r.Y);
}

int main() {
    read_data();
    solve();
}


//karanaggarwal
#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp> // Including tree_order_statistics_node_update

using namespace std;
using namespace __gnu_pbds;

#define TRACE
#ifdef TRACE
#define trace(...) __f(#__VA_ARGS__, __VA_ARGS__)
template <typename Arg1>
void __f(const char* name, Arg1&& arg1){
    cerr << name << " : " << arg1 << std::endl;
}
template <typename Arg1, typename... Args>
void __f(const char* names, Arg1&& arg1, Args&&... args){
    const char* comma = strchr(names + 1, ',');cerr.write(names, comma - names) << " : " << arg1<<" | ";__f(comma+1, args...);
}
#else
#define trace(...)
#endif

#define si(x) scanf("%d",&x)
#define sll(x) scanf("%lld",&x)
#define pi(x) printf("%d\n",x)
#define F first
#define S second
#define PB push_back
#define MP make_pair

typedef long long LL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;

const int mod = 1000000007;
inline void add(int &x, int y){x+=y; if(x>=mod)x-=mod; if(x<0)x+=mod;}
inline int mul(int x, int y){ return ((LL)x * y)%mod;}
int gcd(int a, int b){ if(b)return gcd(b,a%b); return a;}
int power(int a ,int p){int ret = 1; while(p){if(p&1)ret=mul(ret,a); a=mul(a,a); p/=2;}return ret;}
int phi(int n){ int ret=n; int i = 2;
    if(n%i==0){ ret-=ret/i; while(n%i==0)n/=i;}
    for(i=3; i*i<=n; i++)if(n%i==0){ ret-=ret/i; while(n%i==0)n/=i;}
    if(n>1)ret-=ret/n;return ret;
}

typedef tree<PII,null_type,less<PII>,rb_tree_tag,tree_order_statistics_node_update>  OS;

int main()
{
    OS T;
    int n; si(n); while(n--)
    {
        int ch,t,x; si(ch); si(t); si(x);
        if(ch==1)
        {
            T.insert({x,t});
        }
        else if(ch==2)
        {
            T.insert({-x,t});
        }
        else if(ch==3)
        {
            int in = T.order_of_key({x+1,0}) - T.order_of_key({x,0});
            in -=   (T.order_of_key({-x+1,0}) - T.order_of_key({-x,0}));
            pi(in);
        }
    }
    return 0;
}


//karanaggarwal
#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp> // Including tree_order_statistics_node_update

using namespace std;
using namespace __gnu_pbds;

#define TRACE
#ifdef TRACE
#define trace(...) __f(#__VA_ARGS__, __VA_ARGS__)
template <typename Arg1>
void __f(const char* name, Arg1&& arg1){
    cerr << name << " : " << arg1 << std::endl;
}
template <typename Arg1, typename... Args>
void __f(const char* names, Arg1&& arg1, Args&&... args){
    const char* comma = strchr(names + 1, ',');cerr.write(names, comma - names) << " : " << arg1<<" | ";__f(comma+1, args...);
}
#else
#define trace(...)
#endif

#define si(x) scanf("%d",&x)
#define sll(x) scanf("%lld",&x)
#define pi(x) printf("%d\n",x)
#define F first
#define S second
#define PB push_back
#define MP make_pair

typedef long long LL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;

const int mod = 1000000007;
inline void add(int &x, int y){x+=y; if(x>=mod)x-=mod; if(x<0)x+=mod;}
inline int mul(int x, int y){ return ((LL)x * y)%mod;}
int gcd(int a, int b){ if(b)return gcd(b,a%b); return a;}
int power(int a ,int p){int ret = 1; while(p){if(p&1)ret=mul(ret,a); a=mul(a,a); p/=2;}return ret;}
int phi(int n){ int ret=n; int i = 2;
    if(n%i==0){ ret-=ret/i; while(n%i==0)n/=i;}
    for(i=3; i*i<=n; i++)if(n%i==0){ ret-=ret/i; while(n%i==0)n/=i;}
    if(n>1)ret-=ret/n;return ret;
}

typedef tree<PII,null_type,less<PII>,rb_tree_tag,tree_order_statistics_node_update>  OS;

int main()
{
    OS T;
    int n; si(n); while(n--)
    {
        int ch,t,x; si(ch); si(t); si(x);
        if(ch==1)
        {
            T.insert({x,t});
        }
        else if(ch==2)
        {
            T.insert({-x,t});
        }
        else if(ch==3)
        {
            int in = T.order_of_key({x,t}) - T.order_of_key({x,0});
            in -=   (T.order_of_key({-x,t}) - T.order_of_key({-x,0}));
            pi(in);
        }
    }
    return 0;
}


//karanaggarwal
#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp> // Including tree_order_statistics_node_update

using namespace std;
using namespace __gnu_pbds;

#define TRACE
#ifdef TRACE
#define trace(...) __f(#__VA_ARGS__, __VA_ARGS__)
template <typename Arg1>
void __f(const char* name, Arg1&& arg1){
    cerr << name << " : " << arg1 << std::endl;
}
template <typename Arg1, typename... Args>
void __f(const char* names, Arg1&& arg1, Args&&... args){
    const char* comma = strchr(names + 1, ',');cerr.write(names, comma - names) << " : " << arg1<<" | ";__f(comma+1, args...);
}
#else
#define trace(...)
#endif

#define si(x) scanf("%d",&x)
#define sll(x) scanf("%lld",&x)
#define pi(x) printf("%d\n",x)
#define F first
#define S second
#define PB push_back
#define MP make_pair

typedef long long LL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;

const int mod = 1000000007;
inline void add(int &x, int y){x+=y; if(x>=mod)x-=mod; if(x<0)x+=mod;}
inline int mul(int x, int y){ return ((LL)x * y)%mod;}
int gcd(int a, int b){ if(b)return gcd(b,a%b); return a;}
int power(int a ,int p){int ret = 1; while(p){if(p&1)ret=mul(ret,a); a=mul(a,a); p/=2;}return ret;}
int phi(int n){ int ret=n; int i = 2;
    if(n%i==0){ ret-=ret/i; while(n%i==0)n/=i;}
    for(i=3; i*i<=n; i++)if(n%i==0){ ret-=ret/i; while(n%i==0)n/=i;}
    if(n>1)ret-=ret/n;return ret;
}

typedef tree<PII,null_type,less<PII>,rb_tree_tag,tree_order_statistics_node_update>  OS;

int main()
{
    OS T;
    int n; si(n); while(n--)
    {
        int ch,t,x; si(ch); si(t); si(x);
        if(ch==1)
        {
            T.insert({x,t});
        }
        else if(ch==2)
        {
            int in = T.order_of_key({x+1,0}) - T.order_of_key({x,0});
            in -=   (T.order_of_key({-x+1,0}) - T.order_of_key({-x,0}));
            if(in>0)
                T.insert({-x,t});
        }
        else if(ch==3)
        {
            int in = T.order_of_key({x+1,0}) - T.order_of_key({x,0});
            in -=   (T.order_of_key({-x+1,0}) - T.order_of_key({-x,0}));
            pi(in);
        }
    }
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

#define sd(x) scanf("%d", &x)
#define boost ios_base::sync_with_stdio(false);
#define mp make_pair
#define pb push_back
#define all(a) a.begin(), a.end()
#define f first
#define s second

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

const int mod = 1000000007;
const int inf = 50000000;
const int maxn = 100010;

map<int, int> m1, m2[maxn];
map<int, int> :: iterator it;
pair<int, pii> q[maxn];
vector<pair<int, pii> > v[maxn];
vector<int> ft;
int ans[maxn];

void update(int idx, int val)
{
	while(idx<ft.size()){
		ft[idx] = (ft[idx]+val);
		idx+=(idx&(-idx));
	}
}

void range_update(int s, int e, int val)
{
	update(s, val);
	update(e+1, -val);
}

int read(int idx)
{
	int ret = 0;
	while(idx>0){
		ret = (ret+ft[idx]);
		idx-=(idx&(-idx));
	}
	return ret;
}

int main()
{
	//freopen("i.txt", "r", stdin);
	//freopen("o.txt", "w", stdout);
	int m, i, j, t, a, ind, lim;
	scanf("%d", &m);
	for(i=1; i<=m; i++){
		scanf("%d%d%d", &q[i].f, &q[i].s.f, &q[i].s.s);
		m1[q[i].s.s] = 1;
	}
	j = 1;
	for(it=m1.begin(); it!=m1.end(); ++it)
		it->s = j++;
	lim = j;
	for(i=1; i<=m; i++){
		ind = m1[q[i].s.s];
		v[ind].pb(mp(i, mp(q[i].f, q[i].s.f)));
		m2[ind][q[i].s.f] = 1;
	}
	for(i=1; i<lim; i++){
		j = 1;
		for(it=m2[i].begin(); it!=m2[i].end(); ++it)
			it->s = j++;
		ft.resize(j+10, 0);
		for(auto jt:v[i]){
			ind = jt.f;
			a = jt.s.f;
			t = m2[i][jt.s.s];
			//cout << a << " " << ind << " " << t << " " << i << " " << j << "\n";
			if(a==1)
				range_update(t+1, j, 1);
			else if(a==2)
				range_update(t+1, j, -1);
			else
				ans[ind] = read(t);
		}
		ft.clear();
	}
	for(i=1; i<=m; i++){
		if(q[i].f==3)
			printf("%d\n", ans[i]);
	}
	return 0;
}


#include <bits/stdc++.h>

using namespace std;

#define sd(x) scanf("%d", &x)
#define boost ios_base::sync_with_stdio(false);
#define mp make_pair
#define pb push_back
#define all(a) a.begin(), a.end()
#define f first
#define s second

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

const int mod = 1000000007;
const int inf = 50000000;
const int maxn = 100010;

map<int, int> m1, m2[maxn];
map<int, int> :: iterator it;
pair<int, pii> q[maxn];
vector<pair<int, pii> > v[maxn];
vector<int> ft;
int ans[maxn];

void update(int idx, int val)
{
	while(idx<ft.size()){
		ft[idx] = (ft[idx]+val);
		idx+=(idx&(-idx));
	}
}

void range_update(int s, int e, int val)
{
	update(s, val);
	update(e+1, -val);
}

int read(int idx)
{
	int ret = 0;
	while(idx>0){
		ret = (ret+ft[idx]);
		idx-=(idx&(-idx));
	}
	return ret;
}

int main()
{
	//freopen("i.txt", "r", stdin);
	//freopen("o.txt", "w", stdout);
	int m, i, j, t, a, ind, lim;
	scanf("%d", &m);
	for(i=1; i<=m; i++){
		scanf("%d%d%d", &q[i].f, &q[i].s.f, &q[i].s.s);
		m1[q[i].s.s] = 1;
	}
	j = 1;
	for(it=m1.begin(); it!=m1.end(); ++it)
		it->s = j++;
	lim = j;
	for(i=1; i<=m; i++){
		ind = m1[q[i].s.s];
		v[ind].pb(mp(i, mp(q[i].f, q[i].s.f)));
		m2[ind][q[i].s.f] = 1;
	}
	//cout << lim << "\n";
	for(i=1; i<lim; i++){
		j = 1;
		for(it=m2[i].begin(); it!=m2[i].end(); ++it)
			it->s = j++;
		//cout << i << " " << j << "\n";
		ft.resize(j+10, 0);
		for(auto jt:v[i]){
			ind = jt.f;
			a = jt.s.f;
			t = m2[i][jt.s.s];
			//cout << a << " " << t << " " << ind << "\n";
			if(a==1)
				range_update(t+1, j, 1);
			else if(a==2)
				range_update(t+1, j, -1);
			else
				ans[ind] = read(t);
		}
	}
	for(i=1; i<=m; i++){
		if(q[i].f==3)
			printf("%d\n", ans[i]);
	}
	return 0;
}


#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <map>
#include <utility>
#include <ctime>
#include <set>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_set>
#include <cstring>
#include <vector>
#include <algorithm>
#include <functional>
#include <fstream>
#include <sstream>
#include <complex>
#include <stack>
#include <queue>
#include <cstring>
#include <numeric>
#include <cassert>
using namespace std;
typedef long long ll;
#define rep(i,n) for(int (i)=0;(i)<(n);(i)++)
#define rev(i,n) for(int i=(n)-1;(i)>=0;(i)--)
#define all(a) (a).begin(),(a).end()
#define pb(a) push_back(a)
#define bitcount(b) __builtin_popcount(b)
template<typename T, typename S> vector<T>& operator<<(vector<T>& a, S b) { a.push_back(b); return a; }
template<typename T> void operator>>(vector<T>& a, int b) {while(b--)if(!a.empty())a.pop_back();}
bool isprime(int n){ if(n<2)return false;  for(int i=2;i*i<=n;i++)if(n%i==0)return false;  return true;}
ll b_pow(ll x,ll n){return n ? b_pow(x*x,n/2)*(n%2?x:1) : 1ll;}
string itos(int n){stringstream ss;ss << n;return ss.str();}

#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/tag_and_trait.hpp>
using namespace __gnu_pbds;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> set2;
//var_name.find_by_order(k) でk番目（0-indexed）のiteratorを返します。
//var_name.order_of_key(key) でkey以上の最初の要素が、treeの中で何番目か返します。

struct MultiSet{
	map<int,set2> org_set;

	void erase(int v,int t){
		auto it = org_set[v].upper_bound(t);
		--it;
		org_set[v].erase(*it);

	}
	int insert(int v,int t){
		org_set[v].insert(t);
	}
	int count(int v,int t){
		return org_set[v].order_of_key(t);
	}
};

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

	for(int i = 0 ; i < n ; i++){
		int q,t,v;
		scanf("%d%d%d",&q,&t,&v);
		if( q == 1 ){
			myset.insert(v,t);
		}else if( q == 2 ){
			myset.erase(v,t);
		}else if( q == 3 ){
			printf("%d\n",myset.count(v,t));
		}
	}

}


#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <map>
#include <utility>
#include <ctime>
#include <set>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_set>
#include <cstring>
#include <vector>
#include <algorithm>
#include <functional>
#include <fstream>
#include <sstream>
#include <complex>
#include <stack>
#include <queue>
#include <cstring>
#include <numeric>
#include <cassert>
using namespace std;
typedef long long ll;
#define rep(i,n) for(int (i)=0;(i)<(n);(i)++)
#define rev(i,n) for(int i=(n)-1;(i)>=0;(i)--)
#define all(a) (a).begin(),(a).end()
#define pb(a) push_back(a)
#define bitcount(b) __builtin_popcount(b)
template<typename T, typename S> vector<T>& operator<<(vector<T>& a, S b) { a.push_back(b); return a; }
template<typename T> void operator>>(vector<T>& a, int b) {while(b--)if(!a.empty())a.pop_back();}
bool isprime(int n){ if(n<2)return false;  for(int i=2;i*i<=n;i++)if(n%i==0)return false;  return true;}
ll b_pow(ll x,ll n){return n ? b_pow(x*x,n/2)*(n%2?x:1) : 1ll;}
string itos(int n){stringstream ss;ss << n;return ss.str();}

#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/tag_and_trait.hpp>
using namespace __gnu_pbds;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> set2;
//var_name.find_by_order(k) でk番目（0-indexed）のiteratorを返します。
//var_name.order_of_key(key) でkey以上の最初の要素が、treeの中で何番目か返します。

struct MultiSet{
	map<int,set2> ins_set;
	map<int,set2> ers_set;

	void erase(int v,int t){
		ers_set[v].insert(t);
		// ins_set[v].erase(*it);
	}
	int insert(int v,int t){
		ins_set[v].insert(t);
	}
	int count(int v,int t){
		return ins_set[v].order_of_key(t) - ers_set[v].order_of_key(t);
	}
};

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

	for(int i = 0 ; i < n ; i++){
		int q,t,v;
		scanf("%d%d%d",&q,&t,&v);
		if( q == 1 ){
			myset.insert(v,t);
		}else if( q == 2 ){
			myset.erase(v,t);
		}else if( q == 3 ){
			printf("%d\n",myset.count(v,t));
		}
	}

}


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define LOGSZ 22
vector<int> a[1100000];
vector<int> t[1100000];
vector<int> idxans[1100000];
int sorted[1100000];
int ans[1100000];

int tree[(1<<LOGSZ)+1];
int N = (1<<LOGSZ);

// add v to value at x
void set1(int x, int v) {
  while(x <= N) {
    tree[x] += v;
    x += (x & -x);
  }
}

// get cumulative sum up to and including x
int get(int x) {
  int res = 0;
  while(x) {
    res += tree[x];
    x -= (x & -x);
  }
  return res;
}

int main(){
	map<int,int> x2i;
	int n;
	scanf("%d", &n);
	int x[n];
	for(int i = 0; i < n; i++){
		ans[i] = -1;
		int b, c;
		scanf("%d%d%d", &b, &c, &x[i]);
		if(x2i.find(x[i]) == x2i.end()){
			x2i[x[i]] = x2i.size();
		}
		x[i] = x2i[x[i]];
		idxans[x[i]].push_back(i);
		a[x[i]].push_back(b);
		t[x[i]].push_back(c);
	}
	for(int z = 0; z < 1100000; z++){
		if(a[z].size() == 0){
			continue;
		}
		for(int r = 0; r < t[z].size(); r++){
			sorted[r] = t[z][r];
		}
		sort(sorted, sorted+t[z].size());
		map<int,int> q;
		for(int r = 0; r < t[z].size(); r++){
			q[sorted[r]] = r+1;
		}
		for(int r = 0; r < t[z].size(); r++){
			t[z][r] = q[t[z][r]];
			if(a[z][r] == 1){
				set1(t[z][r],1);
			} else if(a[z][r] == 2){
				set1(t[z][r],-1);
			} else {
				ans[idxans[z][r]] = get(t[z][r]);
			}
		}
	}
	for(int i = 0; i < n; i++){
		if(ans[i] >= 0){
			printf("%d\n", ans[i]);
		}
	}
}


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define LOGSZ 22
vector<int> a[1100000];
vector<int> t[1100000];
vector<int> idxans[1100000];
int sorted[1100000];
int ans[1100000];

int tree[(1<<LOGSZ)+1];
int N = (1<<LOGSZ);

// add v to value at x
void set1(int x, int v) {
  while(x <= N) {
    tree[x] += v;
    x += (x & -x);
  }
}

// get cumulative sum up to and including x
int get(int x) {
  int res = 0;
  while(x) {
    res += tree[x];
    x -= (x & -x);
  }
  return res;
}

int main(){
	map<int,int> x2i;
	int n;
	scanf("%d", &n);
	int x[n];
	for(int i = 0; i < n; i++){
		ans[i] = -1;
		int b, c;
		scanf("%d%d%d", &b, &c, &x[i]);
		if(x2i.find(x[i]) == x2i.end()){
			x2i[x[i]] = x2i.size();
		}
		x[i] = x2i[x[i]];
		idxans[x[i]].push_back(i);
		a[x[i]].push_back(b);
		t[x[i]].push_back(c);
	}
	for(int z = 0; z < 1100000; z++){
		if(a[z].size() == 0){
			continue;
		}
		for(int r = 0; r < t[z].size(); r++){
			sorted[r] = t[z][r];
		}
		sort(sorted, sorted+t[z].size());
		map<int,int> q;
		for(int r = 0; r < t[z].size(); r++){
			q[sorted[r]] = r+1;
		}
		for(int r = 0; r < t[z].size(); r++){
			t[z][r] = q[t[z][r]];
			if(a[z][r] == 1){
				set1(t[z][r],1);
			} else if(a[z][r] == 2){
				set1(t[z][r],-1);
			} else {
				ans[idxans[z][r]] = get(t[z][r]);
			}
		}
		for(int r = 0; r < t[z].size(); r++){
			if(a[z][r] == 1){
				set1(t[z][r],-1);
			} else if(a[z][r] == 2){
				set1(t[z][r],1);
			}
		}
	}
	for(int i = 0; i < n; i++){
		if(ans[i] >= 0){
			printf("%d\n", ans[i]);
		}
	}
}


#include <bits/stdc++.h>
using namespace std;
typedef signed long long ll;

#undef _P
#define _P(...) (void)printf(__VA_ARGS__)
#define FOR(x,to) for(x=0;x<(to);x++)
#define FORR(x,arr) for(auto& x:arr)
#define ITR(x,c) for(__typeof(c.begin()) x=c.begin();x!=c.end();x++)
#define ALL(a) (a.begin()),(a.end())
#define ZERO(a) memset(a,0,sizeof(a))
#define MINUS(a) memset(a,0xff,sizeof(a))
//-------------------------------------------------------

int N;
int A[1010101];
int T[1010101];
int V[1010101];
vector<int> E[1010101];
int R[1010101];

template<class V> class BIT_var {
public:
	int ME;
	vector<V> bit;
	void init(int count) {
		ME=1;
		while((1<<ME)<=count*2) ME++;
		bit.clear();
		bit.resize(1<<ME);
	}
	V operator()(int e) {V s=0;e++;while(e) s+=bit[e-1],e-=e&-e; return s;}
	V add(int e,V v) { e++; while(e<=1<<ME) bit[e-1]+=v,e+=e&-e;}
};

void solve() {
	int i,j,k,l,r,x,y; string s;

	scanf("%d",&N);
	FOR(i,N) scanf("%d%d%d",&A[i],&T[i],&V[i]);
	vector<int> VM;
	FOR(i,N) VM.push_back(V[i]);
	sort(ALL(VM));
	VM.erase(unique(ALL(VM)),VM.end());
	FOR(i,N) {
		V[i]=lower_bound(ALL(VM),V[i])-VM.begin();
		E[V[i]].push_back(i);
	}
	FOR(i,1010000) if(E[i].size()) {
		vector<int> TM;
		FORR(r,E[i]) TM.push_back(T[r]);
		sort(ALL(TM));
		TM.erase(unique(ALL(TM)),TM.end());
		FORR(r,E[i]) T[r]=lower_bound(ALL(TM),T[r])-TM.begin();

		BIT_var<int> bit;
		bit.init(E[i].size());
		FORR(x,E[i]) {
			if(A[x]==1) bit.add(T[x],1);
			if(A[x]==2) bit.add(T[x],-1);
			if(A[x]==3) R[x]=bit(T[x]);
		}
	}
	FOR(i,N) if(A[i]==3) _P("%d\n",R[i]);

}

int main(int argc,char** argv){
	string s;int i;
	if(argc==1) ios::sync_with_stdio(false);
	FOR(i,argc-1) s+=argv[i+1],s+='\n';
	FOR(i,s.size()) ungetc(s[s.size()-1-i],stdin);
	solve(); return 0;
}


#include <bits/stdc++.h>
using namespace std;
typedef signed long long ll;

#undef _P
#define _P(...) (void)printf(__VA_ARGS__)
#define FOR(x,to) for(x=0;x<(to);x++)
#define FORR(x,arr) for(auto& x:arr)
#define ITR(x,c) for(__typeof(c.begin()) x=c.begin();x!=c.end();x++)
#define ALL(a) (a.begin()),(a.end())
#define ZERO(a) memset(a,0,sizeof(a))
#define MINUS(a) memset(a,0xff,sizeof(a))
//-------------------------------------------------------

int N;
int A[1010101];
int T[1010101];
int V[1010101];
vector<int> E[1010101];
int R[1010101];

template<class V> class BIT_var {
public:
	int ME;
	vector<V> bit;
	void init(int count) {
		ME=1;
		while((1<<ME)<=count*2) ME++;
		bit.clear();
		bit.resize(1<<ME);
	}
	V operator()(int e) {V s=0;e++;while(e) s+=bit[e-1],e-=e&-e; return s;}
	V add(int e,V v) { e++; while(e<=1<<ME) bit[e-1]+=v,e+=e&-e;}
};

void solve() {
	int i,j,k,l,r,x,y; string s;

	scanf("%d",&N);
	FOR(i,N) scanf("%d%d%d",&A[i],&T[i],&V[i]);
	vector<int> VM;
	FOR(i,N) VM.push_back(V[i]);
	sort(ALL(VM));
	VM.erase(unique(ALL(VM)),VM.end());
	FOR(i,N) {
		V[i]=lower_bound(ALL(VM),V[i])-VM.begin();
		E[V[i]].push_back(i);
	}
	FOR(i,1010000) if(E[i].size()) {
		vector<int> TM;
		FORR(r,E[i]) TM.push_back(T[r]);
		sort(ALL(TM));
		TM.erase(unique(ALL(TM)),TM.end());
		FORR(r,E[i]) T[r]=lower_bound(ALL(TM),T[r])-TM.begin();

		BIT_var<int> bit;
		bit.init(E[i].size());
		FORR(x,E[i]) {
			if(A[x]==1) bit.add(T[x],1);
			if(A[x]==2 || A[x]==3) R[x]=bit(T[x]);
			if(A[x]==2 && R[x]>0) bit.add(T[x],-1);
		}
	}
	FOR(i,N) if(A[i]==3) _P("%d\n",R[i]);

}

int main(int argc,char** argv){
	string s;int i;
	if(argc==1) ios::sync_with_stdio(false);
	FOR(i,argc-1) s+=argv[i+1],s+='\n';
	FOR(i,s.size()) ungetc(s[s.size()-1-i],stdin);
	solve(); return 0;
}


#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define endl '\n'

#define int long long

using namespace std;
using namespace __gnu_pbds;
const int MAXN = (1 << 20);

template <typename T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

struct implicit_treap
{
	struct node
	{
		int sz, val, idx, priority, lazy, rev, sum;
		node *l, *r;

		node() { sz = 0; lazy = 0; rev = 0; val = 0; idx = 0; priority = 0; l = NULL; r = NULL; }
		node(int _val, int _idx)
		{
			val = _val;
			sum = _val;
			rev = 0;
			lazy = 0;
			idx = _idx;
			sz = 1;
			priority = rand();

			l = NULL;
			r = NULL;
		}
	};

	typedef node* pnode;

	pnode root;

	void clear()
	{
		root = NULL;
	}

	implicit_treap() { clear(); }

	int size(pnode p) { return p ? p->sz : 0; }
	void update_size(pnode &p) { if(p) p->sz = size(p->l) + size(p->r) + 1; }

	void lazy(pnode &p)
	{
		if(!p) return;
		p->sum += size(p) * p->lazy;
		p->val += p->lazy;

		if(p->rev) swap(p->l, p->r);

		if(p->l) { p->l->lazy += p->lazy; p->l->rev ^= p->rev; }
		if(p->r) { p->r->lazy += p->lazy; p->r->rev ^= p->rev; }

		p->lazy = 0;
		p->rev = 0;
	}

	void reset(pnode &t) { if(t) t->sum = t->val; }

	void combine(pnode &t, pnode l, pnode r)
	{
		if(!l) { t = r; return; }
		if(!r) { t = l; return; }
		t->sum = l->sum + r->sum;
	}

	void operation(pnode &t)
	{
		if(!t) return;

		reset(t);
		lazy(t->l);
		lazy(t->r);

		combine(t, t->l, t);
		combine(t, t, t->r);
	}

	void split(pnode t, pnode &l, pnode &r, int k)
	{
		if(t == NULL) { l = NULL; r = NULL; return; }
		lazy(t);

		if(t->idx <= k)
			split(t->r, t->r, r, k), l = t;
		else
			split(t->l, l, t->l, k), r = t;

		update_size(t);
		operation(t);
	}

	void merge(pnode &t, pnode l, pnode r)
	{
		lazy(l);
		lazy(r);

		if(!l) { t = r; return; }
		if(!r) { t = l; return; }

		if(l->priority > r->priority)
			merge(l->r, l->r, r), t = l;
		else
			merge(r->l, l, r->l), t = r;

		update_size(t);
		operation(t);
	}

	void insert(int pos, int val)
	{
		if(root == NULL)
		{
			pnode to_add = new node(val, pos);
			root = to_add;
			return;
		}

		pnode l, r, mid;
		mid = new node(val, pos);

		split(root, l, r, pos - 1);
		merge(l, l, mid);
		merge(root, l, r);
	}

	void erase(int qL, int qR)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);
		merge(root, l, r);
	}

	int query(int qL, int qR)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR);

		int answer = mid ? mid->sum : 0;

		merge(r, mid, r);
		merge(root, l, r);

		return answer;
	}

	void update(int qL, int qR, int val)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);

		mid->lazy += val;

		merge(r, mid, r);
		merge(root, l, r);
	}

	void reverse(int qL, int qR)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);

		mid->rev ^= 1;
		merge(r, mid, r);
		merge(root, l, r);
	}

	void cyclic_shift(int qL, int qR, int k)
	{
		if(qL == qR) return;
		k %= (qR - qL + 1);

		pnode l, r, mid, fh, sh;
		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);

		split(mid, fh, sh, (qR - qL + 1) - k - 1);
		merge(mid, sh, fh);

		merge(r, mid, r);
		merge(root, l, r);
	}
};

struct query
{
	int type, tim, val;
	query() { type = 0; tim = 0; val = 0; }
	query(int _type, int _tim, int _val)
	{
		type = _type;
		tim = _tim;
		val = _val;
	}
};

int n;
map<int, int> vals;
vector<query> q;

void read()
{
	cin >> n;

	for(int i = 0; i < n; i++)
	{
		int T, v, it;
		cin >> T >> v >> it;
		vals[it]++;
		q.push_back(query(T, v, it));
	}
}

void compress()
{
	int pos = 1;
	for(auto it = vals.begin(); it != vals.end(); ++it)
		it->second = pos++;

	for(int i = 0; i < n; i++)
		q[i].val = vals[q[i].val];
}

implicit_treap li[MAXN];
int cnt[MAXN];

void solve()
{
	for(int i = 0; i < MAXN; i++)
		li[i].clear();

	compress();

	for(int i = 0; i < n; i++)
	{
		if(q[i].type == 1)
			li[q[i].val].insert(q[i].tim, 1);
		else if(q[i].type == 2)
			li[q[i].val].insert(q[i].tim, -1);
		else
			cout << li[q[i].val].query(0, q[i].tim) << endl;
	}
}

#undef int
int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);

	read();
	solve();
	return 0;
}


#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define endl '\n'

#define int long long

using namespace std;
using namespace __gnu_pbds;
const int MAXN = (1 << 20);

template <typename T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

struct implicit_treap
{
	struct node
	{
		int sz, val, idx, priority, lazy, rev, sum;
		node *l, *r;

		node() { sz = 0; lazy = 0; rev = 0; val = 0; idx = 0; priority = 0; l = NULL; r = NULL; }
		node(int _val, int _idx)
		{
			val = _val;
			sum = _val;
			rev = 0;
			lazy = 0;
			idx = _idx;
			sz = 1;
			priority = rand();

			l = NULL;
			r = NULL;
		}
	};

	typedef node* pnode;

	pnode root;

	void clear()
	{
		root = NULL;
	}

	implicit_treap() { clear(); }

	int size(pnode p) { return p ? p->sz : 0; }
	void update_size(pnode &p) { if(p) p->sz = size(p->l) + size(p->r) + 1; }

	void lazy(pnode &p)
	{
		if(!p) return;
		p->sum += size(p) * p->lazy;
		p->val += p->lazy;

		if(p->rev) swap(p->l, p->r);

		if(p->l) { p->l->lazy += p->lazy; p->l->rev ^= p->rev; }
		if(p->r) { p->r->lazy += p->lazy; p->r->rev ^= p->rev; }

		p->lazy = 0;
		p->rev = 0;
	}

	void reset(pnode &t) { if(t) t->sum = t->val; }

	void combine(pnode &t, pnode l, pnode r)
	{
		if(!l) { t = r; return; }
		if(!r) { t = l; return; }
		t->sum = l->sum + r->sum;
	}

	void operation(pnode &t)
	{
		if(!t) return;

		reset(t);
		lazy(t->l);
		lazy(t->r);

		combine(t, t->l, t);
		combine(t, t, t->r);
	}

	void split(pnode t, pnode &l, pnode &r, int k)
	{
		if(t == NULL) { l = NULL; r = NULL; return; }
		lazy(t);

		if(t->idx <= k)
			split(t->r, t->r, r, k), l = t;
		else
			split(t->l, l, t->l, k), r = t;

		update_size(t);
		operation(t);
	}

	void merge(pnode &t, pnode l, pnode r)
	{
		lazy(l);
		lazy(r);

		if(!l) { t = r; return; }
		if(!r) { t = l; return; }

		if(l->priority > r->priority)
			merge(l->r, l->r, r), t = l;
		else
			merge(r->l, l, r->l), t = r;

		update_size(t);
		operation(t);
	}

	void insert(int pos, int val)
	{
		if(root == NULL)
		{
			pnode to_add = new node(val, pos);
			root = to_add;
			return;
		}

		pnode l, r, mid;
		mid = new node(val, pos);

		split(root, l, r, pos - 1);
		merge(l, l, mid);
		merge(root, l, r);
	}

	void erase(int qL, int qR)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);
		merge(root, l, r);
	}

	int query(int qL, int qR)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR);

		int answer = mid->sum;

		merge(r, mid, r);
		merge(root, l, r);

		return answer;
	}

	void update(int qL, int qR, int val)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);

		mid->lazy += val;

		merge(r, mid, r);
		merge(root, l, r);
	}

	void reverse(int qL, int qR)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);

		mid->rev ^= 1;
		merge(r, mid, r);
		merge(root, l, r);
	}

	void cyclic_shift(int qL, int qR, int k)
	{
		if(qL == qR) return;
		k %= (qR - qL + 1);

		pnode l, r, mid, fh, sh;
		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);

		split(mid, fh, sh, (qR - qL + 1) - k - 1);
		merge(mid, sh, fh);

		merge(r, mid, r);
		merge(root, l, r);
	}
};

struct query
{
	int type, tim, val;
	query() { type = 0; tim = 0; val = 0; }
	query(int _type, int _tim, int _val)
	{
		type = _type;
		tim = _tim;
		val = _val;
	}
};

int n;
map<int, int> vals;
vector<query> q;

void read()
{
	cin >> n;

	for(int i = 0; i < n; i++)
	{
		int T, v, it;
		cin >> T >> v >> it;
		vals[it]++;
		q.push_back(query(T, v, it));
	}
}

void compress()
{
	int pos = 1;
	for(auto it = vals.begin(); it != vals.end(); ++it)
		it->second = pos++;

	for(int i = 0; i < n; i++)
		q[i].val = vals[q[i].val];
}

implicit_treap li[MAXN];
int cnt[MAXN];

void solve()
{
	for(int i = 0; i < MAXN; i++)
		li[i].clear();

	compress();

	for(int i = 0; i < n; i++)
	{
		if(q[i].type == 1)
			li[q[i].val].insert(q[i].tim, 1);
		else if(q[i].type == 2)
			li[q[i].val].insert(q[i].tim, -1);
		else
			cout << li[q[i].val].query(0, q[i].tim) << endl;
	}
}

#undef int
int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);

	read();
	solve();
	return 0;
}


#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define endl '\n'

#define int long long

using namespace std;
using namespace __gnu_pbds;
const int MAXN = (1 << 20);

template <typename T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

struct implicit_treap
{
	struct node
	{
		int sz, val, idx, priority, lazy, rev, sum;
		node *l, *r;

		node() { sz = 0; lazy = 0; rev = 0; val = 0; idx = 0; priority = 0; l = NULL; r = NULL; }
		node(int _val, int _idx)
		{
			val = _val;
			sum = _val;
			rev = 0;
			lazy = 0;
			idx = _idx;
			sz = 1;
			priority = rand();

			l = NULL;
			r = NULL;
		}
	};

	typedef node* pnode;

	pnode root;

	void clear()
	{
		root = NULL;
	}

	implicit_treap() { clear(); }

	int size(pnode p) { return p ? p->sz : 0; }
	void update_size(pnode &p) { if(p) p->sz = size(p->l) + size(p->r) + 1; }

	void lazy(pnode &p)
	{
		if(!p) return;
		p->sum += size(p) * p->lazy;
		p->val += p->lazy;

		if(p->rev) swap(p->l, p->r);

		if(p->l) { p->l->lazy += p->lazy; p->l->rev ^= p->rev; }
		if(p->r) { p->r->lazy += p->lazy; p->r->rev ^= p->rev; }

		p->lazy = 0;
		p->rev = 0;
	}

	void reset(pnode &t) { if(t) t->sum = t->val; }

	void combine(pnode &t, pnode l, pnode r)
	{
		if(!l) { t = r; return; }
		if(!r) { t = l; return; }
		t->sum = l->sum + r->sum;
	}

	void operation(pnode &t)
	{
		if(!t) return;

		reset(t);
		lazy(t->l);
		lazy(t->r);

		combine(t, t->l, t);
		combine(t, t, t->r);
	}

	void split(pnode t, pnode &l, pnode &r, int k)
	{
		if(t == NULL) { l = NULL; r = NULL; return; }
		lazy(t);

		if(t->idx <= k)
			split(t->r, t->r, r, k), l = t;
		else
			split(t->l, l, t->l, k), r = t;

		update_size(t);
		operation(t);
	}

	void merge(pnode &t, pnode l, pnode r)
	{
		lazy(l);
		lazy(r);

		if(!l) { t = r; return; }
		if(!r) { t = l; return; }

		if(l->priority > r->priority)
			merge(l->r, l->r, r), t = l;
		else
			merge(r->l, l, r->l), t = r;

		update_size(t);
		operation(t);
	}

	void insert(int pos, int val)
	{
		if(root == NULL)
		{
			pnode to_add = new node(val, pos);
			root = to_add;
			return;
		}

		pnode l, r, mid;
		mid = new node(val, pos);

		split(root, l, r, pos - 1);
		merge(l, l, mid);
		merge(root, l, r);
	}

	void erase(int qL, int qR)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);
		merge(root, l, r);
	}

	int query(int qL, int qR)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR);

		int answer = mid->sum;

		merge(r, mid, r);
		merge(root, l, r);

		return answer;
	}

	void update(int qL, int qR, int val)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);

		mid->lazy += val;

		merge(r, mid, r);
		merge(root, l, r);
	}

	void reverse(int qL, int qR)
	{
		pnode l, r, mid;

		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);

		mid->rev ^= 1;
		merge(r, mid, r);
		merge(root, l, r);
	}

	void cyclic_shift(int qL, int qR, int k)
	{
		if(qL == qR) return;
		k %= (qR - qL + 1);

		pnode l, r, mid, fh, sh;
		split(root, l, r, qL - 1);
		split(r, mid, r, qR - qL);

		split(mid, fh, sh, (qR - qL + 1) - k - 1);
		merge(mid, sh, fh);

		merge(r, mid, r);
		merge(root, l, r);
	}
};

struct query
{
	int type, tim, val;
	query() { type = 0; tim = 0; val = 0; }
	query(int _type, int _tim, int _val)
	{
		type = _type;
		tim = _tim;
		val = _val;
	}
};

int n;
map<int, int> vals;
vector<query> q;

void read()
{
	cin >> n;

	for(int i = 0; i < n; i++)
	{
		int T, v, it;
		cin >> T >> v >> it;
		vals[it]++;
		q.push_back(query(T, v, it));
	}
}

void compress()
{
	int pos = 1;
	for(auto it = vals.begin(); it != vals.end(); ++it)
		it->second = pos++;

	for(int i = 0; i < n; i++)
		q[i].val = vals[q[i].val];
}

implicit_treap li[MAXN];
int cnt[MAXN];

void solve()
{
	for(int i = 0; i < MAXN; i++)
		li[i].clear();

	compress();

	for(int i = 0; i < n; i++)
	{
		if(q[i].type == 1)
			li[q[i].val].insert(q[i].tim, 1);
		else if(q[i].type == 2)
			li[q[i].val].insert(q[i].tim, -1);
		else
			cout << li[q[i].val].query(0, q[i].tim + 1) << endl;
	}
}

#undef int
int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);

	read();
	solve();
	return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <deque>
#include <queue>
#include <list>
#include <stack>
#include <algorithm>
#include <cassert>

using namespace std;
const int maxn = 1e6+1e2;

struct points
{
	int op , x , y , i;
	points(int op = 0 , int x=0 , int y=0):op(op),x(x),y(y){}
	bool operator <(const points& b)const { return x < b.x; }
};

int n , res[maxn] , now[maxn] , book[maxn];
points a[maxn];

void sorts(points *a , int n , points *b , int m , points *c)
{
	int cnt = 0 , l = 0 , r = 0;
	while(l < n || r < m)
	{
		if(l == n) c[++cnt] = b[++r];
		else if(r == m) c[++cnt] = a[++l];
		else if(a[l+1] < b[r+1]) c[++cnt] = a[++l]; else c[++cnt] = b[++r];
	}
}

points t[maxn] , t1[maxn] , t2[maxn];

void solve(int l , int r)
{
	if(l == r) return;
	int mid = (l+r)/2;
	solve(l , mid);

	int L = 0 , R = 0;
	for(int i=l;i<=mid;i++) if(a[i].op != 3) t1[++L] = a[i];
	for(int i=mid+1;i<=r;i++) if(a[i].op == 3) t2[++R] = a[i] , now[a[i].y] = 0;
	sorts(t1 , L , t2 , R , t);

	L += R;
	for(int i=1;i<=L;i++) if(t[i].op != 3)
	{
		if(t[i].op == 1) now[t[i].y] ++;
		if(t[i].op == 2) now[t[i].y] --;
	}
	else res[t[i].i] += now[t[i].y];

	solve(mid+1 , r);

	//for(int i=1;i<=n;i++) cout<<i[a].op<<" "<<a[i].x<<" "<<a[i].y<<endl; puts("");
	sorts(a+l-1 , mid-l+1 , a+mid , r-mid , t);
	for(int i=l;i<=r;i++) a[i] = t[i-l+1];
}

int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in","r",stdin);
	#endif

	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d%d%d" , &a[i].op , &a[i].x , &a[i].y) , a[i].i = i , book[i] = (a[i].op==3);

	vector<int> all;
	for(int i=1;i<=n;i++) all.push_back(a[i].y);
	sort(all.begin() , all.end());
	all.erase(unique(all.begin() , all.end()) , all.end());
	for(int i=1;i<=n;i++) a[i].y = lower_bound(all.begin() , all.end() , a[i].y) - all.begin();

	solve(1 , n);
	//for(int i=1;i<=n;i++) cout<<a[i].op<<" "<<a[i].x<<" "<<a[i].y<<endl;
	for(int i=1;i<=n;i++) if(book[i]) printf("%d\n" , res[i]);
	return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <deque>
#include <queue>
#include <list>
#include <stack>
#include <algorithm>
#include <cassert>

using namespace std;
const int maxn = 1e6+1e2;

struct points
{
	int op , x , y , i;
	points(int op = 0 , int x=0 , int y=0):op(op),x(x),y(y){}
	bool operator <(const points& b)const { return x < b.x; }
};

int n , res[maxn] , now[maxn];
points a[maxn];

void solve(int l , int r)
{
	if(l == r) return;
	int mid = (l+r)/2;
	solve(l , mid);
	vector<points> all;
	for(int i=l;i<=mid;i++) if(a[i].op != 3) all.push_back(a[i]);
	for(int i=mid+1;i<=r;i++) if(a[i].op == 3) all.push_back(a[i]) , now[a[i].y] = 0;

	sort(all.begin() , all.end());
	for(int i=0;i<all.size();i++) if(all[i].op != 3)
	{
		if(all[i].op == 1) now[all[i].y] ++;
		if(all[i].op == 2) now[all[i].y] --;
	}
	else res[all[i].i] += now[all[i].y];

	solve(mid+1 , r);
}

int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in","r",stdin);
	#endif

	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d%d%d" , &a[i].op , &a[i].x , &a[i].y) , a[i].i = i;

	vector<int> all;
	for(int i=1;i<=n;i++) all.push_back(a[i].y);
	sort(all.begin() , all.end());
	all.erase(all.begin() , all.end());
	for(int i=1;i<=n;i++) a[i].y = lower_bound(all.begin() , all.end() , a[i].y) - all.begin();

	solve(1 , n);
	for(int i=1;i<=n;i++) if(a[i].op == 3) printf("%d\n" , res[i]);
	return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <deque>
#include <queue>
#include <list>
#include <stack>
#include <algorithm>
#include <cassert>

using namespace std;
const int maxn = 1e6+1e2;

struct points
{
	int op , x , y , i;
	points(int op = 0 , int x=0 , int y=0):op(op),x(x),y(y){}
	bool operator <(const points& b)const { return x < b.x; }
};

int n , res[maxn] , now[maxn] , book[maxn];
points a[maxn];

void sorts(points *a , int n , points *b , int m , points *c)
{
	int cnt = 0 , l = 0 , r = 0;
	while(l < n || r < m)
	{
		if(l == n) c[++cnt] = b[++r];
		else if(r == m) c[++cnt] = a[++l];
		else if(a[l+1] < b[r+1]) c[++cnt] = a[++l]; else c[++cnt] = b[++r];
	}
}

points t[maxn] , t1[maxn] , t2[maxn];

void solve(int l , int r)
{
	if(l == r) return;
	int mid = (l+r)/2;
	solve(l , mid);

	int L = 0;// , R = 0;
	for(int i=l;i<=mid;i++) if(a[i].op != 3) t[++L] = a[i];
	for(int i=mid+1;i<=r;i++) if(a[i].op == 3) t[++L] = a[i] , now[a[i].y] = 0;
	//sorts(t1 , L , t2 , R , t);
	sort(t+1 , t+1+L);
	//L += R;
	for(int i=1;i<=L;i++) if(t[i].op != 3)
	{
		if(t[i].op == 1) now[t[i].y] ++;
		if(t[i].op == 2) now[t[i].y] --;
	}
	else res[t[i].i] += now[t[i].y];

	solve(mid+1 , r);
	sort(a+l , a+r+1);
	//sorts(a+l-1 , mid-l+1 , a+mid , r-mid , t);
	//for(int i=l;i<=r;i++) a[i] = t[i-l+1];
}

int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in","r",stdin);
	#endif

	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d%d%d" , &a[i].op , &a[i].x , &a[i].y) , a[i].i = i , book[i] = (a[i].op==3);

	vector<int> all;
	for(int i=1;i<=n;i++) all.push_back(a[i].y);
	sort(all.begin() , all.end());
	all.erase(unique(all.begin() , all.end()) , all.end());
	for(int i=1;i<=n;i++) a[i].y = lower_bound(all.begin() , all.end() , a[i].y) - all.begin();

	solve(1 , n);
	for(int i=1;i<=n;i++) if(book[i]) printf("%d\n" , res[i]);
	return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <deque>
#include <queue>
#include <list>
#include <stack>
#include <algorithm>
#include <cassert>

using namespace std;
const int maxn = 1e5+1e2;

struct points
{
	int op , x , y , i;
	points(int op = 0 , int x=0 , int y=0):op(op),x(x),y(y){}
	bool operator <(const points& b)const { return x < b.x; }
};

int n , res[maxn] , now[maxn];
points a[maxn];

void solve(int l , int r)
{
	if(l == r) return;
	int mid = (l+r)/2;
	solve(l , mid);
	vector<points> all;
	for(int i=l;i<=mid;i++) if(a[i].op != 3) all.push_back(a[i]);
	for(int i=mid+1;i<=r;i++) if(a[i].op == 3) all.push_back(a[i]) , now[a[i].y] = 0;

	sort(all.begin() , all.end());
	for(int i=0;i<all.size();i++) if(all[i].op != 3)
	{
		if(all[i].op == 1) now[all[i].y] ++;
		if(all[i].op == 2) now[all[i].y] --;
	}
	else res[all[i].i] += now[all[i].y];

	solve(mid+1 , r);
}

int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in","r",stdin);
	#endif

	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d%d%d" , &a[i].op , &a[i].x , &a[i].y) , a[i].i = i;

	solve(1 , n);
	for(int i=1;i<=n;i++) if(a[i].op == 3) printf("%d\n" , res[i]);
	return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <deque>
#include <queue>
#include <list>
#include <stack>
#include <algorithm>
#include <cassert>

using namespace std;
const int maxn = 1e6+1e2;

struct points
{
	int op , x , y , i;
	points(int op = 0 , int x=0 , int y=0):op(op),x(x),y(y){}
	bool operator <(const points& b)const { return x < b.x; }
};

int n , res[maxn] , now[maxn] , book[maxn];
points a[maxn];

void sorts(points *a , int n , points *b , int m , points *c)
{
	int cnt = 0 , l = 0 , r = 0;
	while(l < n || r < m)
	{
		if(l == n) c[++cnt] = b[++r];
		else if(r == m) c[++cnt] = a[++l];
		else if(a[l+1] < b[r+1]) c[++cnt] = a[++l]; else c[++cnt] = b[++r];
	}
}

points t[maxn] , t1[maxn] , t2[maxn];

void solve(int l , int r)
{
	if(l == r) return;
	int mid = (l+r)/2;
	solve(l , mid);
	solve(mid+1 , r);

	int L = 0 , R = 0;
	for(int i=l;i<=mid;i++) if(a[i].op != 3) t1[++L] = a[i];
	for(int i=mid+1;i<=r;i++) if(a[i].op == 3) t2[++R] = a[i] , now[a[i].y] = 0;
	sorts(t1 , L , t2 , R , t);

	L += R;
	for(int i=1;i<=L;i++) if(t[i].op != 3)
	{
		if(t[i].op == 1) now[t[i].y] ++;
		if(t[i].op == 2) now[t[i].y] --;
	}
	else res[t[i].i] += now[t[i].y];

	sorts(a+l-1 , mid-l+1 , a+mid , r-mid , t);
	for(int i=l;i<=r;i++) a[i] = t[i-l+1];
}

int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in","r",stdin);
	#endif

	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d%d%d" , &a[i].op , &a[i].x , &a[i].y) , a[i].i = i , book[i] = (a[i].op==3);

	vector<int> all;
	for(int i=1;i<=n;i++) all.push_back(a[i].y);
	sort(all.begin() , all.end());
	all.erase(unique(all.begin() , all.end()) , all.end());
	for(int i=1;i<=n;i++) a[i].y = lower_bound(all.begin() , all.end() , a[i].y) - all.begin();

	solve(1 , n);
	for(int i=1;i<=n;i++) if(book[i]) printf("%d\n" , res[i]);
	return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <deque>
#include <queue>
#include <list>
#include <stack>
#include <algorithm>
#include <cassert>

using namespace std;
const int maxn = 1e6+1e2;

struct points
{
	int op , x , y , i;
	points(int op = 0 , int x=0 , int y=0):op(op),x(x),y(y){}
	bool operator <(const points& b)const { return x < b.x; }
};

int n , res[maxn] , now[maxn];
points a[maxn];

void solve(int l , int r)
{
	if(l == r) return;
	int mid = (l+r)/2;
	solve(l , mid);
	vector<points> all;
	for(int i=l;i<=mid;i++) if(a[i].op != 3) all.push_back(a[i]);
	for(int i=mid+1;i<=r;i++) if(a[i].op == 3) all.push_back(a[i]) , now[a[i].y] = 0;

	sort(all.begin() , all.end());
	for(int i=0;i<all.size();i++) if(all[i].op != 3)
	{
		if(all[i].op == 1) now[all[i].y] ++;
		if(all[i].op == 2) now[all[i].y] --;
	}
	else res[all[i].i] += now[all[i].y];

	solve(mid+1 , r);
}

int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in","r",stdin);
	#endif

	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d%d%d" , &a[i].op , &a[i].x , &a[i].y) , a[i].i = i;

	vector<int> all;
	for(int i=1;i<=n;i++) all.push_back(a[i].y);
	sort(all.begin() , all.end());
	all.erase(unique(all.begin() , all.end()) , all.end());
	for(int i=1;i<=n;i++) a[i].y = lower_bound(all.begin() , all.end() , a[i].y) - all.begin();

	solve(1 , n);
	for(int i=1;i<=n;i++) if(a[i].op == 3) printf("%d\n" , res[i]);
	return 0;
}


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

map<int , map<int , int> > tree;

inline int lb(int k){return k&(-k);}

void modify(map<int,int>& t , int ind , int d)
{
	while(ind <= 1000000000)
	{
		t[ind] +=d;
		ind += lb(ind);
	}
}
int getsum(map<int,int>& t , int ind)
{
	int res = 0;
	while(ind >= 1)
	{
		res += t[ind];
		ind -= lb(ind);
	}
	return res;
}

int a[100001] , tim[100001] , x[100001];

int main()
{
	int n;scanf("%d" , &n);
	for(int i = 1 ; i <= n ; i++)
	{
		scanf("%d%d%d" , &a[i] , &tim[i] , &x[i]);
	}
	for(int i = 1 ; i <= n ; i++)
	{
		if(a[i] == 1)
		{
			/*
			if(tree.find(x[i]) == tree.end())
				tree[x[i]] = new map<int,int>;
			*/
			modify(tree[x[i]] , tim[i] + 1 , 1);
		}
		if(a[i] == 2)
		{
			/*
			if(tree.find(x[i]) == tree.end())
				tree[x[i]] = new map<int,int>;
			*/
			modify(tree[x[i]] , tim[i] + 1 , -1);
		}
		if(a[i] == 3)
		{
			/*
			if(tree.find(x[i]) == tree.end())
				tree[x[i]] = new map<int,int>;
			*/
			printf("%d\n" , getsum(tree[x[i]] , tim[i]));
		}
	}
	return 0;
}


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

map<int , int> tree[100001];

inline int lb(int k){return k&(-k);}

void modify(map<int,int>& t , int ind , int d)
{
	while(ind <= 1000000000)
	{
		t[ind] +=d;
		ind += lb(ind);
	}
}
int getsum(map<int,int>& t , int ind)
{
	int res = 0;
	while(ind >= 1)
	{
		res += t[ind];
		ind -= lb(ind);
	}
	return res;
}

struct Inf{int* f;int v;}t[100001];
bool operator < (const Inf& t1 , const Inf& t2)
{return t1.v < t2.v;}

int a[100001] , tim[100001] , x[100001];

int main()
{
	int n;scanf("%d" , &n);
	for(int i = 1 ; i <= n ; i++)
	{
		scanf("%d%d%d" , &a[i] , &tim[i] , &x[i]);
		t[i].f = &x[i];
	}
	sort(t + 1 , t + n + 1);
	(*t[1].f) = 1;
	for(int i = 2 ; i <= n ; i++)
	{
		(*t[i].f) = (*t[i - 1].f);
		if(t[i].v > t[i - 1].v)++(*t[i].f);
	}
	for(int i = 1 ; i <= n ; i++)
	{
		if(a[i] == 1)
		{
			modify(tree[x[i]] , tim[i] , 1);
		}
		if(a[i] == 2)
		{
			modify(tree[x[i]] , tim[i] , -1);
		}
		if(a[i] == 3)
			printf("%d\n" , getsum(tree[x[i]] , tim[i]));
	}
	return 0;
}


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

map<int , int> tree[100001];

inline int lb(int k){return k&(-k);}

void modify(map<int,int>& t , int ind , int d)
{
	while(ind <= 1000000000)
	{
		t[ind] +=d;
		ind += lb(ind);
	}
}
int getsum(map<int,int>& t , int ind)
{
	int res = 0;
	while(ind >= 1)
	{
		res += t[ind];
		ind -= lb(ind);
	}
	return res;
}

struct Inf{int* f;int v;}t[100001];
bool operator < (const Inf& t1 , const Inf& t2)
{return t1.v < t2.v;}

int a[100001] , tim[100001] , x[100001];

int main()
{
	int n;scanf("%d" , &n);
	for(int i = 1 ; i <= n ; i++)
	{
		scanf("%d%d%d" , &a[i] , &tim[i] , &x[i]);
		t[i].f = &x[i];t[i].v = x[i];
	}
	sort(t + 1 , t + n + 1);
	(*t[1].f) = 1;
	for(int i = 2 ; i <= n ; i++)
	{
		(*t[i].f) = (*t[i - 1].f);
		if(t[i].v > t[i - 1].v)++(*t[i].f);
	}
	for(int i = 1 ; i <= n ; i++)
	{
		if(a[i] == 1)
		{
			modify(tree[x[i]] , tim[i] + 1 , 1);
		}
		if(a[i] == 2)
		{
			modify(tree[x[i]] , tim[i] + 1 , -1);
		}
		if(a[i] == 3)
			printf("%d\n" , getsum(tree[x[i]] , tim[i]));
	}
	return 0;
}


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

map<int , int> tree[100001];

inline int lb(int k){return k&(-k);}

void modify(map<int,int>& t , int ind , int d)
{
	while(ind <= 1000000000)
	{
		t[ind] +=d;
		ind += lb(ind);
	}
}
int getsum(map<int,int>& t , int ind)
{
	int res = 0;
	while(ind >= 1)
	{
		res += t[ind];
		ind -= lb(ind);
	}
	return res;
}

struct Inf{int* f;int v;}t[100001];
bool operator < (const Inf& t1 , const Inf& t2)
{return t1.v < t2.v;}

int a[100001] , tim[100001] , x[100001];

int main()
{
	int n;scanf("%d" , &n);
	for(int i = 1 ; i <= n ; i++)
	{
		scanf("%d%d%d" , &a[i] , &tim[i] , &x[i]);
		t[i].f = &x[i];
	}
	sort(t + 1 , t + n + 1);
	(*t[1].f) = 1;
	for(int i = 2 ; i <= n ; i++)
	{
		(*t[i].f) = (*t[i - 1].f);
		if(t[i].v > t[i - 1].v)++(*t[i].f);
	}
	for(int i = 1 ; i <= n ; i++)
	{
		if(a[i] == 1)
		{
			modify(tree[x[i]] , tim[i] + 1 , 1);
		}
		if(a[i] == 2)
		{
			modify(tree[x[i]] , tim[i] + 1 , -1);
		}
		if(a[i] == 3)
			printf("%d\n" , getsum(tree[x[i]] , tim[i]));
	}
	return 0;
}


#define HEADER_H
//#define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
#define REP(i,n)	for(auto i=decltype(n)(0); i<(n); ++i)
#define all(v)		begin(v), end(v)
using namespace std;		using ull= unsigned long long;
using ll = long long;		using ld = long double;
using vi = vector<ll>;		using vvi= vector<vi>;
using vb = vector<bool>;	using ii = pair<ll,ll>;
constexpr bool LOG = true;
void Log() { if(LOG) cerr << "\n"; }
template<class T, class... S> void Log(T t, S... s){
	if(LOG) cerr << t << "\t", Log(s...);
} /* ============== END OF HEADER ============== */

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using order_tree = tree<
	ll, null_type, less<ll>, // key, value types. TOut can be null_type
	rb_tree_tag, tree_order_statistics_node_update>;

int main(){
	ios_base::sync_with_stdio(false); cin.tie(nullptr);
	ll n; cin >> n;

	map<ll, order_tree> m;

	while(n--){
		ll a,t,x;
		cin >> a >> t >> x;
		if(a == 1) m[x].insert(t);
		if(a == 2){
			auto it = prev(m[x].lower_bound(t));
			m[x].erase(it);
		}
		if(a == 3){
			cout << m[x].order_of_key(t) << '\n';
		}
	}

	return 0;
}


#define HEADER_H
//#define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
#define REP(i,n)	for(auto i=decltype(n)(0); i<(n); ++i)
#define all(v)		begin(v), end(v)
using namespace std;		using ull= unsigned long long;
using ll = long long;		using ld = long double;
using vi = vector<ll>;		using vvi= vector<vi>;
using vb = vector<bool>;	using ii = pair<ll,ll>;
constexpr bool LOG = true;
void Log() { if(LOG) cerr << "\n"; }
template<class T, class... S> void Log(T t, S... s){
	if(LOG) cerr << t << "\t", Log(s...);
} /* ============== END OF HEADER ============== */

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using order_tree = tree<
	ll, null_type, less<ll>, // key, value types. TOut can be null_type
	rb_tree_tag, tree_order_statistics_node_update>;

int main(){
	ios_base::sync_with_stdio(false); cin.tie(nullptr);
	ll n; cin >> n;

	map<ll, order_tree> i;
	map<ll, order_tree> o;

	while(n--){
		ll a,t,x;
		cin >> a >> t >> x;
		if(a == 1) i[x].insert(t);
		if(a == 2) o[x].insert(t);
		if(a == 3){
			cout << i[x].order_of_key(t) - o[x].order_of_key(t) << '\n';
		}
	}

	return 0;
}


#define HEADER_H
//#define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
#define REP(i,n)	for(auto i=decltype(n)(0); i<(n); ++i)
#define all(v)		begin(v), end(v)
using namespace std;		using ull= unsigned long long;
using ll = long long;		using ld = long double;
using vi = vector<ll>;		using vvi= vector<vi>;
using vb = vector<bool>;	using ii = pair<ll,ll>;
constexpr bool LOG = true;
void Log() { if(LOG) cerr << "\n"; }
template<class T, class... S> void Log(T t, S... s){
	if(LOG) cerr << t << "\t", Log(s...);
} /* ============== END OF HEADER ============== */

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using order_tree = tree<
	ll, null_type, less<ll>, // key, value types. TOut can be null_type
	rb_tree_tag, tree_order_statistics_node_update>;

int main(){
	ios_base::sync_with_stdio(false); cin.tie(nullptr);
	ll n; cin >> n;

	map<ll, order_tree> m;

	while(n--){
		ll a,t,x;
		cin >> a >> t >> x;
		if(a == 1) m[x].insert(t);
		if(a == 2){
			auto it = prev(m[x].find(t));
			m[x].erase(it);
		}
		if(a == 3){
			cout << m[x].order_of_key(t) << '\n';
		}
	}

	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+N,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;
}