#include <stdio.h>
#include <algorithm>
using namespace std;
#define NMAX 100000
#define MOD 301
struct xy{
int x, y, p, sol;
};
struct comp{
bool operator()(const xy &a, const xy &b){
if(a.y == b.y) return a.x < b.x;
return a.y > b.y;
}
};
const int lin[]={0,0,1,-1};
const int col[]={1,-1,0,0};
xy v[NMAX], w[20010], cop[20010];
int ans[20010];
int A[MOD][MOD];
int T[NMAX], RG[NMAX];
int n, k;
int find(int x){
if(T[x] != x)
T[x] = find(T[x]);
return T[x];
}
void unire(int x, int y){
if(RG[x] > RG[y])
T[y] = x;
else T[x] = y;
if(RG[x] == RG[y]) RG[y]++;
}
void vecini(int nod, int h){
int x = nod/MOD, y = nod%MOD;
int p;
for(int t = 0; t < 4; ++t)
if(A[x+lin[t]][y+col[t]] >= h){
p = (x+lin[t])*MOD + y+col[t];
if(find(nod) != find(p))
unire(find(nod), find(p));
}
}
void divide(int p, int q, int st, int dr, int poz){
if(p > q) return;
if(st > dr) return;
int m = (st+dr)/2;
int poz2, i;
if(poz == 1)
for(i = 1; i <= v[0].x; i++)
T[v[i].x] = v[i].x;
for(i = poz; i <= v[0].x && v[i].y >= m; i++)
vecini(v[i].x, m);
poz2 = i ;
int nr = p-1, nr2 = 0;
for(i = p; i <= q; ++i)
if(find(w[i].x) != find(w[i].y))
w[++nr] = w[i];
else {
cop[++nr2] = w[i];
cop[nr2].sol = max(w[i].sol, m);
}
for(i = nr+1; i <= nr + nr2; ++i)
w[i] = cop[i-nr];
divide(p, nr, st, m-1, poz2);
divide(nr+1, nr+nr2, m+1, dr, 1);
}
int main(){
freopen("matrice2.in", "r", stdin);
freopen("matrice2.out", "w", stdout);
scanf("%d%d", &n, &k);
int hmax = 0;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j){
v[++v[0].x].x = i*MOD + j;
scanf("%d", &v[v[0].x].y);
A[i][j] = v[v[0].x].y;
if(v[v[0].x].y > hmax) hmax = v[v[0].x].y;
}
sort(v + 1, v + v[0].x + 1, comp());
for(int i = 1; i <= k; ++i){
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
w[i].x = x1*MOD + y1;
w[i].y = x2*MOD + y2;
w[i].p = i;
}
divide(1, k, 1, hmax, 1);
for(int i = 1; i <= k; ++i)
ans[w[i].p] = w[i].sol;
for(int i = 1; i <= k; ++i)
printf("%d\n", ans[i]);
return 0;
}