#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f, mod = 1e9 + 7;
const int maxn = 1e6 + 100, mxn = 5e5 + 100;
const long double PI = acos(-1.0);
long long gcd(long long a, long long b) { return (a ? gcd(b % a, a) : b); }
void exgcd(long long a, long long b, long long& d, long long& x, long long& y) {
  if (!b) {
    d = a;
    x = 1;
    y = 0;
  } else {
    exgcd(b, a % b, d, y, x);
    y -= x * (a / b);
  }
}
long long power(long long a, long long n) {
  long long p = 1;
  while (n > 0) {
    if (n % 2) {
      p = p * a;
    }
    n >>= 1;
    a *= a;
  }
  return p;
}
long long power(long long a, long long n, long long mod) {
  long long p = 1;
  while (n > 0) {
    if (n % 2) {
      p = p * a;
      p %= mod;
    }
    n >>= 1;
    a *= a;
    a %= mod;
  }
  return p % mod;
}
int n, m, k;
int kind[maxn], tim[maxn], kindd[maxn], timm[maxn];
vector<int> have[maxn], que[maxn];
int a[maxn], t[maxn], x[maxn], tt[maxn], anser[maxn];
struct node {
  int r, l;
  int s;
} se[maxn << 2];
void build(int tr, int l, int s) {
  se[tr].r = s;
  se[tr].l = l;
  se[tr].s = 0;
  int mid = (s + l) / 2;
  if (s == l) return;
  build(tr * 2, l, mid);
  build(tr * 2 + 1, mid + 1, s);
  return;
}
void inse(int tr, int i, int d) {
  if (se[tr].l == se[tr].r) {
    se[tr].s += d;
    return;
  }
  int mid = (se[tr].r + se[tr].l) / 2;
  if (i <= mid)
    inse(tr * 2, i, d);
  else
    inse(tr * 2 + 1, i, d);
  se[tr].s = se[tr << 1].s + se[tr << 1 | 1].s;
  return;
}
int query(int tr, int i, int j) {
  if (se[tr].l >= i && se[tr].r <= j) {
    return se[tr].s;
  }
  int mid = (se[tr].r + se[tr].l) / 2;
  int s1 = 0, s2 = 0;
  if (i <= mid) s1 = query(tr * 2, i, j);
  if (j > mid) s2 = query(tr * 2 + 1, i, j);
  return s1 + s2;
}
int main() {
  int q;
  while (~scanf("%d", &n)) {
    int cun = 0, ans = 1;
    for (int i = 1; i <= n; i++) {
      scanf("%d%d%d", &a[i], &t[i], &x[i]);
      tt[i] = t[i];
      kind[i] = x[i];
    }
    sort(kind + 1, kind + 1 + n);
    kind[0] = -1;
    for (int i = 1; i <= n; i++) {
      if (kind[i] != kind[i - 1]) {
        kindd[cun++] = kind[i];
      }
    }
    sort(tt + 1, tt + 1 + n);
    for (int i = 1; i <= n; i++) {
      int g = lower_bound(kindd, kindd + cun, x[i]) - kindd;
      if (a[i] == 1 || a[i] == 2) {
        have[g].push_back(i);
      } else {
        que[g].push_back(i);
      }
    }
    tt[0] = -1;
    for (int i = 1; i <= n; i++)
      if (tt[i] != tt[i - 1]) tim[ans++] = tt[i];
    build(1, 1, ans);
    for (int i = 0; i < cun; i++) {
      int ss = 0, st = 0;
      for (int j = 0; j < que[i].size(); j++) {
        int d = que[i][j];
        if (st < have[i].size())
          ss = have[i][st];
        else
          ss = n + 1;
        while (ss < d) {
          int g = lower_bound(tim + 1, tim + ans, t[ss]) - tim;
          if (a[ss] == 1) {
            inse(1, g, 1);
          } else
            inse(1, g, -1);
          st++;
          if (st >= have[i].size())
            break;
          else
            ss = have[i][st];
        }
        int g = lower_bound(tim + 1, tim + ans, t[d]) - tim;
        int gg = query(1, 1, g);
        anser[d] = gg;
      }
      for (int j = 0; j < st; j++) {
        int g = lower_bound(tim + 1, tim + ans, t[have[i][j]]) - tim;
        if (a[have[i][j]] == 2) {
          inse(1, g, 1);
        } else
          inse(1, g, -1);
      }
    }
    for (int i = 1; i <= n; i++)
      if (a[i] == 3) {
        printf("%d\n", anser[i]);
      }
  }
  return 0;
}
