#include <bits/stdc++.h>
using namespace std;
const int maxsize = 2e6;
template <class Tnode>
struct SegTree {
  typedef Tnode (*seg_tree_func)(Tnode a, Tnode b);
  Tnode *data = nullptr;
  seg_tree_func f;
  Tnode default_val;
  int tsize;
  SegTree() {}
  SegTree(int size, seg_tree_func _f, Tnode _default_val) {
    f = _f;
    default_val = _default_val;
    tsize = 1;
    while (tsize < 4 * size) tsize *= 2;
    data = new Tnode[tsize];
    fill(data, data + tsize, default_val);
  }
  void put(Tnode val, int pos, int root, int lbound, int rbound) {
    if (pos < lbound || pos > rbound) return;
    if (lbound < rbound) {
      put(val, pos, root * 2, lbound, lbound + (rbound - lbound) / 2);
      put(val, pos, root * 2 + 1, lbound + (rbound - lbound) / 2 + 1, rbound);
      data[root] = f(data[root * 2], data[root * 2 + 1]);
    } else
      data[root] = f(val, data[root]);
  }
  void put(Tnode val, int pos) { put(val, pos, 1, 0, tsize / 2 - 1); }
  Tnode get(int l, int r, int root, int lbound, int rbound) {
    if (r < lbound || l > rbound) return default_val;
    if (l <= lbound && rbound <= r) return data[root];
    Tnode lc_val = get(l, r, root * 2, lbound, lbound + (rbound - lbound) / 2);
    Tnode rc_val =
        get(l, r, root * 2 + 1, lbound + (rbound - lbound) / 2 + 1, rbound);
    return f(lc_val, rc_val);
  }
  Tnode get(int l, int r) { return get(l, r, 1, 0, tsize / 2 - 1); }
  void clear() { delete[] data; }
};
map<int, int> zip_time;
map<int, vector<int>> queries;
int cur_processing_number = 0;
pair<int, int> sum(pair<int, int> f, pair<int, int> s) {
  if (f.second == s.second && f.second == cur_processing_number)
    return make_pair(f.first + s.first, f.second);
  if (f.second == cur_processing_number) return f;
  if (s.second == cur_processing_number) return s;
  return make_pair(0, cur_processing_number);
}
int a[maxsize], t[maxsize], x[maxsize];
int ans[maxsize];
int main() {
  int n;
  scanf("%d", &n);
  vector<int> times;
  times.reserve(n);
  for (int i = 0; i < n; i++) {
    scanf("%d%d%d", &a[i], &t[i], &x[i]);
    queries[x[i]].push_back(i);
    times.push_back(t[i]);
  }
  sort(times.begin(), times.end());
  times.resize(unique(times.begin(), times.end()) - times.begin());
  for (int i = 0; i < (int)times.size(); i++) zip_time[times[i]] = i + 1;
  SegTree<pair<int, int>> tree =
      SegTree<pair<int, int>>(times.size(), sum, make_pair(0, -1));
  for (auto i : queries) {
    cur_processing_number = i.first;
    for (auto j : i.second) {
      int zipped_time = zip_time[t[j]];
      if (a[j] == 1) {
        tree.put(make_pair(1, cur_processing_number), zipped_time);
      } else if (a[j] == 2) {
        tree.put(make_pair(-1, cur_processing_number), zipped_time);
      } else {
        int quan = tree.get(1, zipped_time).first;
        ans[j] = quan;
      }
    }
  }
  for (int i = 0; i < n; i++) {
    if (a[i] == 3) printf("%d\n", ans[i]);
  }
}
