#include <bits/stdc++.h>
#pragma comment(linker, "/STACK:256000000")
using namespace std;
const int INF = 0x3f3f3f3f;
const long long LINF = 0x3f3f3f3f3f3f3f3f;
const int MAX = 1e5 + 10;
struct TreapNode {
  TreapNode *l, *r, *p;
  int y;
  int rangeSize;
  int d;
  pair<int, int> value;
  int rangeValue;
  TreapNode() {}
  static int getRangeSize(TreapNode *t) { return t == NULL ? 0 : t->rangeSize; }
  static int getRangeValue(TreapNode *t) {
    return t == NULL ? 0 : t->rangeValue;
  }
  static void update(TreapNode *t) {
    t->rangeSize = getRangeSize(t->l) + getRangeSize(t->r) + 1;
    t->rangeValue = getRangeValue(t->l) + getRangeValue(t->r) + t->d;
  }
  TreapNode(pair<int, int> _value, int d)
      : d(d), y((1 << 16) * rand() + rand()), value(_value) {
    l = r = p = NULL;
    update(this);
  }
  struct iterator {
    TreapNode *curr;
    iterator(TreapNode *_curr) : curr(_curr) {}
    iterator &operator++() {
      assert(curr != NULL);
      TreapNode *t = curr->r;
      if (t != NULL)
        while (t->l != NULL) t = t->l;
      else {
        for (t = curr; t->p != NULL && t->p->r == t; t = t->p)
          ;
        t = t->p;
      }
      curr = t;
      return *this;
    }
    bool operator!=(const TreapNode::iterator &other) {
      return curr != other.curr;
    }
    bool operator==(const TreapNode::iterator &other) {
      return curr == other.curr;
    }
    TreapNode *operator*() { return curr; }
  };
};
const int MAXSTACKSIZE = 1e5 + 100;
struct TreapNodeStack {
  TreapNode stack[MAXSTACKSIZE];
  int size;
  TreapNodeStack() { size = 0; }
  TreapNode *getNext() { return &stack[size++]; }
};
struct Treap {
  TreapNodeStack *s;
  TreapNode *root;
  Treap(TreapNodeStack *_s) : s(_s), root(NULL) {}
  Treap(TreapNodeStack *_s, TreapNode *_root) : s(_s), root(_root) {}
  static TreapNode *merge(TreapNode *t1, TreapNode *t2) {
    if (t1 == NULL) return t2;
    if (t2 == NULL) return t1;
    TreapNode *t;
    if (t1->y > t2->y) {
      t = t1;
      t->r = merge(t->r, t2);
      t->r->p = t;
    } else {
      t = t2;
      t->l = merge(t1, t->l);
      t->l->p = t;
    }
    TreapNode::update(t);
    return t;
  }
  static void splitByValue(TreapNode *t, pair<int, int> value, TreapNode *&L,
                           TreapNode *&R) {
    if (t == NULL) return void(L = R = NULL);
    if (t->value < value) {
      splitByValue(t->r, value, t->r, R);
      if (t->r != NULL) t->r->p = t;
      if (R != NULL) R->p = NULL;
      L = t;
    } else {
      splitByValue(t->l, value, L, t->l);
      if (t->l != NULL) t->l->p = t;
      if (L != NULL) L->p = NULL;
      R = t;
    }
    TreapNode::update(t);
  }
  static void splitByIndex(TreapNode *t, int idx, TreapNode *&L,
                           TreapNode *&R) {
    if (t == NULL) return void(L = R = NULL);
    int currentIndex = TreapNode::getRangeSize(t->l);
    if (idx <= currentIndex) {
      splitByIndex(t->l, idx, L, t->l);
      if (t->l != NULL) t->l->p = t;
      if (L != NULL) L->p = NULL;
      R = t;
    } else {
      splitByIndex(t->r, idx - currentIndex - 1, t->r, R);
      if (t->r != NULL) t->r->p = t;
      if (R != NULL) R->p = NULL;
      L = t;
    }
    TreapNode::update(t);
  }
  TreapNode::iterator begin() {
    TreapNode *t = root;
    while (t != NULL && t->l != NULL) t = t->l;
    return TreapNode::iterator(t);
  }
  TreapNode::iterator end() { return TreapNode::iterator(NULL); }
  TreapNode::iterator lower_bound(pair<int, int> value) {
    TreapNode *l, *r, *t;
    splitByValue(root, value, l, r);
    for (t = r; t != NULL && t->l != NULL; t = t->l)
      ;
    TreapNode::iterator res = TreapNode::iterator(t);
    root = merge(l, r);
    return res;
  }
  TreapNode::iterator find(TreapNode *t, pair<int, int> value) {
    if (t == NULL) return this->end();
    if (t->value == value) return TreapNode::iterator(t);
    return t->value < value ? find(t->r, value) : find(t->l, value);
  }
  TreapNode::iterator find(pair<int, int> value) { return find(root, value); }
  TreapNode *operator[](int i) {
    assert(i < TreapNode::getRangeSize(root) && i >= 0);
    TreapNode *l, *r, *t;
    splitByIndex(root, i, l, r);
    for (t = r; t != NULL && t->l != NULL; t = t->l)
      ;
    merge(l, r);
    return t;
  }
  void erase(pair<int, int> value) {
    if (find(value) == end()) return;
    TreapNode *l, *r, *tmp;
    splitByValue(root, value, l, r);
    splitByIndex(r, 1, tmp, r);
    root = merge(l, r);
  }
  void clear() { root = NULL; }
  size_t size() { return TreapNode::getRangeSize(root); }
  void insert(pair<int, int> value, int d) {
    TreapNode *t = s->getNext();
    (*t) = TreapNode(value, d);
    TreapNode *l, *r;
    splitByValue(root, value, l, r);
    l = merge(l, t);
    root = merge(l, r);
  }
  void out() { out(root); }
  void out(TreapNode *t, int lvl = 1) {
    return;
    if (lvl == 1)
      ;
    if (t == NULL) return;
    out(t->l, lvl + 1);
    ;
    out(t->r, lvl + 1);
  }
};
int nq;
TreapNodeStack st;
void clear() {}
int getRng(Treap &t, int x, int time) {
  TreapNode *l, *r;
  Treap::splitByValue(t.root, make_pair(x, 0), l, r);
  TreapNode *lr, *rr;
  Treap::splitByValue(r, make_pair(x, time), lr, rr);
  int res = TreapNode::getRangeValue(lr);
  r = Treap::merge(lr, rr);
  t.root = Treap::merge(l, r);
  return res;
}
void solve() {
  Treap t1(&st);
  Treap t2(&st);
  for (int i = 0; i < nq; i++) {
    int type, time, x;
    scanf("%d%d%d", &type, &time, &x);
    if (type == 1) {
      t1.insert(make_pair(x, time), 1);
    } else if (type == 2) {
      t2.insert(make_pair(x, time), 1);
    } else {
      int l = getRng(t1, x, time);
      int r = getRng(t2, x, time);
      int res = l - r;
      assert(res >= 0);
      printf("%d\n", res);
    }
  }
}
int main() {
  srand(time(NULL));
  scanf("%d", &nq);
  clear();
  solve();
  return 0;
}
