#include <bits/stdc++.h>
using namespace std;
template <typename A, typename B>
inline bool chmax(A &a, B b) {
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template <typename A, typename B>
inline bool chmin(A &a, B b) {
  if (a > b) {
    a = b;
    return 1;
  }
  return 0;
}
using ll = long long;
using pii = pair<int, int>;
constexpr ll INF = 1ll << 30;
constexpr ll longINF = 1ll << 60;
constexpr ll MOD = 1000000007;
constexpr bool debug = 0;
template <typename T, std::size_t M>
struct PersistentArray {
  static_assert(M > 0);
  using value_type = T;
  using const_reference = const value_type &;
  using size_type = std::size_t;

 private:
  struct Node;
  using sptr_type = std::shared_ptr<Node>;
  using node_ptr = Node *;
  using const_ptr = const Node *;
  struct Node {
    sptr_type childs[M];
    value_type val;
    Node(const_reference val) : val(val) {}
  };

 private:
  sptr_type root;
  value_type def_val;

 private:
  PersistentArray(sptr_type &&root, const_reference def_val)
      : root(root), def_val(def_val) {}

 public:
  PersistentArray(const_reference def_val = 0)
      : root(nullptr), def_val(def_val) {}
  PersistentArray set(size_type k, const_reference x) const {
    const_ptr node = root.get();
    sptr_type new_root =
        std::make_shared<Node>(k == 0 ? x : (node ? node->val : def_val));
    node_ptr new_node = new_root.get();
    for (; k > 0; k /= M) {
      const size_type m = k % M;
      if (node) {
        for (size_type i = 0; i < M; ++i) {
          if (i != m) new_node->childs[i] = node->childs[i];
        }
        new_node->childs[m] = std::make_shared<Node>(
            k < M ? x : (node->childs[m] ? node->childs[m]->val : def_val));
        node = node->childs[m].get();
      } else {
        new_node->childs[m] = std::make_shared<Node>(k < M ? x : def_val);
      }
      new_node = new_node->childs[m].get();
    }
    if (node) {
      for (size_type i = 0; i < M; ++i) new_node->childs[i] = node->childs[i];
    }
    return {std::move(new_root), def_val};
  }
  void destructive_set(size_type k, const_reference x) {
    if (!root) root = std::make_shared<Node>(k == 0 ? x : def_val);
    node_ptr node = root.get();
    for (; k >= M; k /= M) {
      const size_type m = k % M;
      if (!node->childs[m]) node->childs[m] = std::make_shared<Node>(def_val);
      node = node->childs[m].get();
    }
    if (node->childs[k])
      node->childs[k]->val = x;
    else
      node->childs[k] = std::make_shared<Node>(x);
  }
  const_reference get(size_type k) const {
    const_ptr node = root.get();
    while (k > 0 && node) {
      node = node->childs[k % M].get();
      k /= M;
    }
    return k == 0 && node ? node->val : def_val;
  }
};
int main() {
  int N, M, Q;
  scanf("%d %d %d", &N, &M, &Q);
  using parray = PersistentArray<int, 2>;
  struct Data {
    parray shelf, inv, sum;
    int allsum;
  };
  vector<Data> pr(1);
  pr[0].shelf = parray(0);
  pr[0].inv = parray(0);
  pr[0].sum = parray(0);
  pr[0].allsum = 0;
  while (Q--) {
    int q;
    scanf("%d", &q);
    Data cur = pr.back();
    if (q == 1 || q == 2) {
      int x, y;
      scanf("%d %d", &y, &x);
      --y;
      --x;
      int st = q == 1;
      int idx = y * M + x;
      int cur_state = cur.shelf.get(idx);
      int exist = cur_state ^ cur.inv.get(y);
      if (st ^ exist) {
        cur.sum = cur.sum.set(y, cur.sum.get(y) + (st ? 1 : -1));
        cur.allsum += st ? 1 : -1;
        cur.shelf = cur.shelf.set(idx, !cur_state);
      }
    } else {
      int k;
      scanf("%d", &k);
      if (q == 3) {
        --k;
        int csum = cur.sum.get(k);
        cur.sum = cur.sum.set(k, M - csum);
        cur.inv = cur.inv.set(k, !cur.inv.get(k));
        cur.allsum += -csum + (M - csum);
      } else
        cur = pr[k];
    }
    printf("%d\n", cur.allsum);
    pr.emplace_back(cur);
  }
  return 0;
}
