#include<cstdio>
#include<algorithm>
const int MAXN = 500000 * 5 + 5;
//x,y:横縦座標
//type:操作タイプ
//add:矩形領域の面積を求めるためにいくつの矩形を加減するので、addは正負を示します
//id:問い合わせのid、なぜなら一つの問い合わせがいくつかに分かれたからです
//ans:現在の問い合わせに対応する答えを保存します
struct node {
int x, y, type, add, id, ans;
} a[MAXN], tmp[MAXN];
int n, m, newp;
int ans[MAXN];
void add (int x, int y, int type, int add, int id, int ans) {
a[++newp] = (node){x, y, type, add, id, ans};
}
bool cmp1 (node x, node y) {
if (x.x == y.x) {
if (x.y == y.y) {//自分よりyが大きいのを見つけたら停止
return x.type < y.type;//問い合わせの前に修正
}
return x.y < y.y;
}
return x.x < y.x;
}
void cdq (int l, int r) {
if (l == r) {
return;
}
int mid = (l + r) >> 1;
cdq(l, mid);
cdq(mid + 1, r);
//左半分が処理されたので、左半分のすべての修正を右半分の問い合わせに加えます
int newl = l, newr = mid + 1, pos = l, ans = 0;
//この区間の上層区間では、左側のxは必ず右側より小さいので、ここでyでソートしてaに戻します
while (newl <= mid && newr <= r) {//越境しないように
if (a[newl].y <= a[newr].y) {//newlの操作がnewrの範囲内であることを確認します
if (a[newl].type == 1) {
++ans;//点で、答えを累積します
}
tmp[pos++] = a[newl++];
}
else {
if (a[newr].type == 2) {
a[newr].ans += ans;//問い合わせで、前に統計した点を加えます
}
tmp[pos++] = a[newr++];
}
}
//処理が終わっていないものを残さない
while (newl <= mid) {
tmp[pos++] = a[newl++];
}
while (newr <= r) {
if (a[newr].type == 2) {
a[newr].ans += ans;
}
tmp[pos++] = a[newr++];
}
//yでソートされた結果をaに格納します
for (int i = l; i <= r; ++i) {
a[i] = tmp[i];
}
}
int main (void) {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
int x, y;
scanf("%d%d", &x, &y);
add(x, y, 1, 0, 0, 0);
}
for (int i = 1; i <= m; ++i) {
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
add(x2, y2, 2, 1, i, 0);
add(x1 - 1, y2, 2, -1, i, 0);
add(x2, y1 - 1, 2, -1, i, 0);
add(x1 - 1, y1 - 1, 2, 1, i, 0);
}
std::sort(a + 1, a + 1 + newp, cmp1);
cdq(1, newp);
for (int i = 1; i <= newp; ++i) {
if (a[i].type == 2) {
ans[a[i].id] += a[i].add * a[i].ans;
}
}
for (int i = 1; i <= m; ++i) {
printf("%d\n", ans[i]);
}
return 0;
}