#include <bits/stdc++.h>
using namespace std;
const double EPS = 1e-9;
const long long INF = 1e17;
struct Query {
  int a, t, x;
};
vector<vector<int> > fTrees;
inline void update(vector<int> &a, int pos, int x) {
  while (pos < (int)a.size()) {
    a[pos] += x;
    pos = pos | (pos + 1);
  }
}
inline int get(vector<int> &a, int r) {
  int sum = 0;
  while (r >= 0) {
    sum += a[r];
    r = (r & (r + 1)) - 1;
  }
  return sum;
}
int main() {
  ios::sync_with_stdio(0);
  cin.tie(nullptr);
  cout.tie(nullptr);
  int n;
  cin >> n;
  vector<Query> queries(n);
  vector<int> nums;
  for (int i = 0; i < n; i++) {
    cin >> queries[i].a >> queries[i].t >> queries[i].x;
    nums.push_back(queries[i].x);
  }
  sort((nums).begin(), (nums).end());
  nums.resize(unique((nums).begin(), (nums).end()) - nums.begin());
  vector<vector<int> > times(nums.size());
  fTrees.resize(nums.size());
  for (int i = 0; i < n; i++) {
    int ind =
        lower_bound((nums).begin(), (nums).end(), queries[i].x) - nums.begin();
    times[ind].push_back(queries[i].t);
  }
  for (int i = 0; i < (int)nums.size(); i++) {
    sort((times[i]).begin(), (times[i]).end());
    fTrees[i].resize(times[i].size(), 0);
  }
  for (auto q : queries) {
    int numInd = lower_bound((nums).begin(), (nums).end(), q.x) - nums.begin();
    if (q.a == 1) {
      int timeInd =
          lower_bound((times[numInd]).begin(), (times[numInd]).end(), q.t) -
          times[numInd].begin();
      update(fTrees[numInd], timeInd, 1);
    } else if (q.a == 2) {
      int timeInd =
          lower_bound((times[numInd]).begin(), (times[numInd]).end(), q.t) -
          times[numInd].begin();
      update(fTrees[numInd], timeInd, -1);
    } else {
      int timeInd =
          lower_bound((times[numInd]).begin(), (times[numInd]).end(), q.t) -
          times[numInd].begin();
      cout << get(fTrees[numInd], timeInd) << "\n";
    }
  }
  return 0;
}
