#include <bits/stdc++.h>
using namespace std;
vector<string> split(const string& s, char c) {
  vector<string> v;
  stringstream ss(s);
  string x;
  while (getline(ss, x, c)) v.emplace_back(x);
  return move(v);
}
void err(vector<string>::iterator it) {}
template <typename T, typename... Args>
void err(vector<string>::iterator it, T a, Args... args) {
  cerr << a << ' ';
  err(++it, args...);
}
static constexpr int inf = std::numeric_limits<int>::max();
const int M = 100010;
struct BIT {
  vector<int> tree;
  void update(int idx, int val) {
    while (idx < tree.size()) {
      tree[idx] += val;
      idx += idx & (-idx);
    }
  }
  int query(int idx) {
    int sum = 0;
    while (idx > 0) {
      sum += tree[idx];
      idx -= idx & (-idx);
    }
    return sum;
  }
};
struct data {
  int a, t, first;
  data(int p = 0, int q = 0, int r = 0) {
    a = p;
    t = q;
    first = r;
  }
};
data q[M];
BIT bit[M];
vector<int> tms[M];
map<int, int> num;
vector<int>::iterator it;
int main() {
  int i, j, k, n, a, b, c, id = 0;
  scanf("%d", &n);
  for (i = 0; i < n; i++) {
    scanf("%d%d%d", &a, &b, &c);
    q[i] = data(a, b, c);
    if (num[c] == 0) num[c] = ++id;
  }
  id++;
  for (i = 0; i < n; i++) {
    q[i].first = num[q[i].first];
    tms[q[i].first].push_back(q[i].t);
  }
  for (i = 0; i < id; i++) sort(tms[i].begin(), tms[i].end());
  for (i = 0; i < id; i++) bit[i].tree.resize(tms[i].size() + 5);
  for (i = 0; i < n; i++) {
    int first = q[i].first;
    int pos = lower_bound(tms[first].begin(), tms[first].end(), q[i].t) -
              tms[first].begin() + 1;
    if (q[i].a == 1) {
      bit[first].update(pos, 1);
    } else if (q[i].a == 2) {
      bit[first].update(pos, -1);
    } else {
      printf("%d\n", bit[first].query(pos));
    }
  }
  return 0;
}
