#include <bits/stdc++.h>
using namespace std;
ifstream fin("matrice2.in");
ofstream fout("matrice2.out");
const int nmax = 303;
const int qmax = 20003;
int NRMAX = -1;
int n,nr,it,m;
int matrix[nmax][nmax];
int toVector[nmax][nmax];
bool activ[nmax][nmax];
struct nodStr
{
int i,j,tata,val,h;
bool operator < (const nodStr &a) const
{
return val < a.val;
}
} v[nmax*nmax];
struct queryStr
{
int xs,ys,xf,yf,rasp,i;
} queries[qmax];
struct queryNodeStr
{
int st,dr,h;
queue<int> q;
};
void resetFather()
{
it = nr;
memset(activ,0,sizeof activ);
for(int i=1; i<=nr; i++)
{
v[i].tata = i;
v[i].h = 0;
}
}
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int tabs(int a)
{
while(a!=v[a].tata) a = v[a].tata;
}
void uneste(int a, int b)
{
int ta = tabs(a), tb = tabs(b);
if(v[ta].h < v[tb].h)
{
swap(ta,tb);
swap(a,b);
}
v[tb].tata = ta;
v[ta].h += (v[ta].h == v[tb].h);
int pas = a;
while(pas!=ta)
{
int aux = v[pas].tata;
v[pas].tata = ta;
pas = aux;
}
pas = b;
while(pas!=tb)
{
int aux = v[pas].tata;
v[pas].tata = tb;
pas = aux;
}
}
bool ok(int i, int j)
{
return 1<=i && i<=n && 1<=j && j<=n;
}
void add_until(int mij)
{
for(; v[it].val>=mij && it>=1; it--)
{
activ[v[it].i][v[it].j] = 1;
//cout<<"unesc "<<v[it].i<<" | "<<v[it].j<<" ("<<v[it].val<<")\n";
for(int d=0; d<4; d++)
{
int pi = v[it].i + dx[d];
int pj = v[it].j + dy[d];
if(ok(pi,pj) && activ[pi][pj])
{
uneste(toVector[pi][pj],it);
}
}
}
}
void bfs()
{
queue<queryNodeStr> q;
q.push({1,NRMAX,0});
int prevH = 0;
for(int i=1; i<=m; i++)
q.front().q.push(i);
while(!q.empty())
{
queryNodeStr curr = q.front();
q.pop();
int st = curr.st,dr = curr.dr, h = curr.h;
//cout<<st<<" "<<(st+dr)/2<<" "<<dr<<"\n";
//cout<<prevH<<"h"<<h<<"\n";
if(prevH != h) resetFather();
if(st==dr)
{
while(!curr.q.empty())
{
//cout<<":"<<curr.q.front()<<"\n";
queries[curr.q.front()].rasp = st;
curr.q.pop();
}
}
else
{
int mij = (st + dr) / 2;
queryNodeStr toLeft,toRight;
toLeft = {st, mij, h + 1};
toRight = {mij+1, dr, h + 1};
add_until(mij+1);
while(!curr.q.empty())
{
//cout<<":"<<curr.q.front()<<"\n";
queryStr qry = queries[curr.q.front()];
curr.q.pop();
int t1 = tabs(toVector[qry.xs][qry.ys]);
int t2 = tabs(toVector[qry.xf][qry.yf]);
if(t1 == t2)
{
//cout<<"cee "<<v[t1].i<<" "<<v[t1].j<<"\n";
//cout<<"cee "<<v[t2].i<<" "<<v[t2].j<<"\n";
//cout<<qry.xs<<" si "<<qry.ys<<" |na| "<<qry.xf<<" si "<<qry.yf<<"\n";
toRight.q.push(qry.i);
}
else toLeft.q.push(qry.i);
}
add_until(st);
if(!toRight.q.empty())
q.push(toRight);
if(!toLeft.q.empty())
q.push(toLeft);
}
prevH = h;
}
}
int main()
{
fin>>n>>m;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
fin>>matrix[i][j];
v[++nr] = {i,j,0,matrix[i][j],0};
NRMAX = max(NRMAX, matrix[i][j]);
}
}
sort(v + 1, v + nr + 1);
for(int i=1; i<=nr; i++)
{
toVector[v[i].i][v[i].j] = i;
}
resetFather();
for(int i=1; i<=m; i++)
{
int xs,ys,xf,yf;
fin>>xs>>ys>>xf>>yf;
queries[i] = {xs,ys,xf,yf,0,i};
}
bfs();
for(int i=1; i<=m; i++)
{
fout<<queries[i].rasp<<"\n";
}
return 0;
}