#include <cstdio>
#include <algorithm>
#include <cstring>
#define NMax 305
#define QMax 20005
using namespace std;
struct cell
{
int x, y, h;
};
struct query
{
int s, sx, sy, fx, fy, i;
};
const int DX[]={-1, 0, 1, 0}, DY[]={0, 1, 0, -1};
cell Cell[NMax*NMax]; query Q[QMax];
int N, NQ, F[NMax*NMax], Map[NMax][NMax];
inline bool CompareC (const cell &C1, const cell &C2)
{
return C1.h>C2.h;
}
inline bool CompareS (const query &Q1, const query &Q2)
{
return Q1.s>Q2.s;
}
inline bool CompareI (const query &Q1, const query &Q2)
{
return Q1.i<Q2.i;
}
inline int Find (int X)
{
int R;
for (R=X; F[R]!=R; R=F[R]);
for (int Y; F[X]!=R; Y=X, X=F[X], F[Y]=R);
return R;
}
inline void Merge (int X, int Y)
{
int RX=Find (X), RY=Find (Y);
if (RX!=RY) F[RY]=RX;
}
inline void Insert (int X, int Y)
{
F[Map[X][Y]]=Map[X][Y];
for (int d=0; d<4; ++d)
{
int NX=X+DX[d], NY=Y+DY[d];
if (F[Map[NX][NY]]==0) continue;
Merge (Map[X][Y], Map[NX][NY]);
}
}
void Solve ()
{
sort (Cell+1, Cell+N*N+1, CompareC);
for (int Step=30; Step>=0; --Step)
{
memset (F, 0, sizeof (F)); sort (Q+1, Q+NQ+1, CompareS);
for (int i=1, q=1; q<=NQ; ++q)
{
for (; Q[q].s+(1<<Step)<=Cell[i].h and i<=N*N; ++i) Insert (Cell[i].x, Cell[i].y);
int RS=Find (Map[Q[q].sx][Q[q].sy]), RF=Find (Map[Q[q].fx][Q[q].fy]);
if (RS==RF and RS!=0 and RF!=0) Q[q].s+=(1<<Step);
}
}
sort (Q+1, Q+NQ+1, CompareI);
}
void Read ()
{
freopen ("matrice2.in", "r", stdin);
scanf ("%d %d", &N, &NQ);
for (int i=1; i<=N; ++i)
{
for (int j=1; j<=N; ++j)
{
Map[i][j]=(i-1)*N+(j-1)+1;
Cell[Map[i][j]].x=i, Cell[Map[i][j]].y=j;
scanf ("%d", &Cell[Map[i][j]].h);
}
}
for (int i=1; i<=NQ; ++i) scanf ("%d %d %d %d", &Q[i].sx, &Q[i].sy, &Q[i].fx, &Q[i].fy), Q[i].i=i;;
}
void Print ()
{
freopen ("matrice2.out", "w", stdout);
for (int i=1; i<=NQ; ++i) printf ("%d\n", Q[i].s);
}
int main()
{
Read ();
Solve ();
Print ();
return 0;
}