#pragma GCC optimize("Ofast","-funroll-loops","-fdelete-null-pointer-checks")
#pragma GCC target("ssse3","sse3","sse2","sse","avx2","avx")
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
using namespace std;

#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1 << 21], *p1 = buf, *p2 = buf;

inline int qread() {
	register char c = getchar();
	register int x = 0, f = 1;
	while (c < '0' || c > '9') {
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		x = (x << 3) + (x << 1) + c - 48;
		c = getchar();
	}
	return x * f;
}

inline int Abs(const int& x) {return (x > 0 ? x : -x);}
inline int Max(const int& x, const int& y) {return (x > y ? x : y);}
inline int Min(const int& x, const int& y) {return (x < y ? x : y);}

const int S = 317, N = 100405;

int val[N], f[N];
struct DSU {
	int siz[N], rt[N];
	inline int GetRoot(int v) {
		if (f[v] == v) return v;
		return (f[v] = GetRoot(f[v]));
	}
	inline void Merge(int p, int x, int y) {
		if (rt[y]) {
			f[rt[x] + (p - 1) * S] = rt[y] + (p - 1) * S;
		} else {
			rt[y] = rt[x];
			val[rt[y] + (p - 1) * S] = y;
		}
		siz[y] += siz[x]; rt[x] = siz[x] = 0;
	}
};
DSU d[S + 5];
int n, m, a[N], pos[N], mx[N], tag[N];

inline void Read() {
	n = qread(); m = qread();
	for (register int i = 1;i <= n;i++) pos[i] = (i - 1) / S + 1, d[pos[i]].siz[a[i] = qread()]++;
}

inline void Rebuild(int bid) {
	for (register int j = (bid - 1) * S + 1;j <= Min(bid * S, n);j++) {
		a[j] = val[d[bid].GetRoot(j)];
		d[bid].siz[a[j]] = d[bid].rt[a[j]] = 0;
		a[j] -= tag[bid];
	}
	for (register int j = (bid - 1) * S + 1;j <= Min(bid * S, n);j++) f[j] = 0;
	tag[bid] = 0;
}

inline void Maintain(int bid) {
	mx[bid] = 0;
	for (register int i = (bid - 1) * S + 1;i <= bid * S;i++) {
		mx[bid] = Max(mx[bid], a[i]);
		if (!d[bid].rt[a[i]]) f[i] = i, d[bid].rt[a[i]] = i - (bid - 1) * S, val[i] = a[i];
		else f[i] = d[bid].rt[a[i]] + (bid - 1) * S;
		d[bid].siz[a[i]]++;
	}
}

inline void Prefix() {
	for (register int i = 1;i <= pos[n];i++) Maintain(i);
}

inline void Modify(int bl, int l, int r, int x) {
	Rebuild(bl);
	for (register int j = l;j <= r;j++) {
		if (a[j] > x) a[j] -= x;
	}
	Maintain(bl);
}

inline void ModifyBlk(int bi, int x) {
	if (mx[bi] - tag[bi] >= (x << 1)) {
		for (register int j = tag[bi] + 1;j <= tag[bi] + x;j++) d[bi].Merge(bi, j, j + x);
		tag[bi] += x;
	} else {
		for (register int j = mx[bi];j > tag[bi] + x;j--) d[bi].Merge(bi, j, j - x);
		mx[bi] = Min(tag[bi] + x, mx[bi]);
	}
}

inline int qryd(int bl, int l, int r, int x) {
	register int ans = 0;
	for (register int j = l;j <= r;j++) {
		if (val[d[bl].GetRoot(j)] - tag[bl] == x) ans++;
	}
	return ans;
}

inline int qryblk(int bi, int x) {
	if (x + tag[bi] > 100000) return 0;
	return d[bi].siz[x + tag[bi]];
}

inline void Solve() {
	for (register int i = 1;i <= m;i++) {
		register int opt = qread(), l = qread(), r = qread(), x = qread();
		if (opt == 1) {
			register int bl = pos[l], br = pos[r];
			if (bl == br) {
				Modify(bl, l, r, x);
			} else {
				Modify(bl, l, bl * S, x);
				Modify(br, (br - 1) * S + 1, r, x);
				for (register int j = bl + 1;j < br;j++) ModifyBlk(j, x);
			}
		} else {
			register int bl = pos[l], br = pos[r];
			if (bl == br) {
				printf("%d\n", qryd(bl, l, r, x));
			} else {
				register int ans = qryd(bl, l, bl * S, x) + qryd(br, (br - 1) * S + 1, r, x);
				for (register int j = bl + 1;j < br;j++) ans += qryblk(j, x);
				printf("%d\n", ans);
			}
		}
	}
}

int main() {
	Read();
	Prefix();
	Solve();
	#ifndef ONLINE_JUDGE
	while (1);
	#endif
	return 0;
}
