#include <bits/stdc++.h>
template <typename T>
T nextInt() {
  T x = 0, p = 1;
  char ch;
  do {
    ch = getchar();
  } while (ch <= ' ');
  if (ch == '-') {
    p = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    x = x * 10 + (ch - '0');
    ch = getchar();
  }
  return x * p;
}
const int maxN = (int)7e3 + 10;
const int maxV = (int)1e6 + 10;
const int INF = (int)1e9;
const int mod = (int)1e9 + 7;
const long long llinf = (long long)1e18;
struct adding {
  int l, r;
  int x;
  adding() {}
  adding(int l, int r, int x) : l(l), r(r), x(x) {}
};
std::vector<adding> addings[maxN];
int n, q;
void addOnInterval(int level, int l, int r, int x) {
  addings[level].push_back(adding(l, r, x));
}
int getLeft(int l) {
  if (l == 1) return 1;
  int L = 0, R = 20;
  while (R - 1 > L) {
    int M = (L + R) / 2;
    if ((1 << M) < l) {
      L = M;
    } else {
      R = M;
    }
  }
  return l + L + 1;
}
int getRight(int r) {
  if (r & (r - 1)) return getLeft(r);
  return 1 + getLeft(r);
}
int last[maxV];
int curQuery = 0;
int calcSetSize(int level, int l, int r) {
  int result = 0;
  while (level <= n) {
    for (size_t i = 0; i < addings[level].size(); ++i) {
      int l2 = addings[level][i].l;
      int r2 = addings[level][i].r;
      int x = addings[level][i].x;
      if (!(l > r2 || r < l2)) {
        if (last[x] != curQuery) {
          result++;
        }
        last[x] = curQuery;
      }
    }
    l = getLeft(l);
    r = getRight(r);
    level++;
  }
  return result;
}
int main() {
  n = nextInt<int>();
  q = nextInt<int>();
  for (curQuery = 1; curQuery <= q; ++curQuery) {
    int t = nextInt<int>();
    int level = nextInt<int>();
    if (t == 1) {
      int l = nextInt<int>();
      int r = nextInt<int>();
      int x = nextInt<int>();
      addOnInterval(level, l, r, x);
    } else {
      int v = nextInt<int>();
      printf("%d\n", calcSetSize(level, v, v));
    }
  }
  return 0;
}
