#include<stdio.h>
#include<vector>
#include<bitset>
#include<algorithm>
using namespace std;
#define Nmax 305
#define Mmax 20005
#define NRmax 100005
#define log 25
struct querry
{
int x1,x2,y1,y2,sol;
} v[Mmax];
int N,M,m[Nmax][Nmax];
int Q[log][Mmax];
int poz[NRmax],arb[NRmax],NR,viz[NRmax],leg;
vector <int> l[NRmax];
void vecini(int k)
{
if (k/N!=N-1)
{
l[k].push_back(k+N);
l[k+N].push_back(k);
}
if (k%N!=N-1)
{
l[k].push_back(k+1);
l[k+1].push_back(k);
}
}
int find(int x)
{
int y=x,z;
for(; arb[x]>=0 ;x=arb[x]);
while( arb[y]>=0 )
{
z=arb[y];
arb[y]=x;
y=z;
}
return x;
}
void unite(int x,int y)
{
for(;arb[x]>=0;x=arb[x]);
arb[x]=y;
}
void caut(int k,int in,int sf,int st,int dr,int P)
{
int mid=st+(dr-st)/2,p1,p2,leg;
if (!P)
for(int i=0;i<=NR;++i)
arb[i]=-1,viz[i]=0;
for(int i=P; m[poz[i]/N][poz[i]%N] >= mid && i<=NR;++i,++P)
{
viz[ poz[i] ]=1;
for(int j=0;j<(int)l[poz[i]].size();++j)
{
leg=l[poz[i]][j];
if (viz[ leg ] && find(poz[i])!=find(leg))
unite(poz[i],leg);
}
}
p1=in-1; p2=sf+1;
for(int i=in;i<=sf;++i)
if (find( N*v[Q[k][i]].x1+v[Q[k][i]].y1 )==find( N*v[Q[k][i]].x2+v[Q[k][i]].y2 ))
{
Q[k+1][++p1]=Q[k][i];
v[Q[k][i]].sol= mid;
}
else
Q[k+1][--p2]=Q[k][i];
if (st<=mid-1 && p2<=sf) caut(k+1,p2,sf,st,mid-1,P);
if (mid+1<=dr && in<=p1) caut(k+1,in,p1,mid+1,dr,0);
}
bool cmp(int i,int j)
{
return (m[i/N][i%N] > m[j/N][j%N]);
}
void solve()
{
NR=N*N-1;
for(int i=0;i<=NR;++i)
{
vecini(i);
poz[i]=i;
}
sort(poz,poz+NR+1,cmp);
int Hmax=m[poz[0]/N][poz[0]%N];
for(int i=1;i<=M;++i)
Q[0][i]=i;
caut(0,1,M,1,Hmax,0);
for(int i=1;i<=M;++i)
printf("%d\n",v[i].sol);
}
void cit()
{
freopen("matrice2.in","r",stdin);
freopen("matrice2.out","w",stdout);
scanf("%d%d",&N,&M);
for(int i=0;i<N;++i)
for(int j=0;j<N;++j)
scanf("%d",&m[i][j]);
for(int i=1;i<=M;++i)
{
scanf("%d%d%d%d",&v[i].x1,&v[i].y1,&v[i].x2,&v[i].y2);
v[i].x1--;
v[i].x2--;
v[i].y1--;
v[i].y2--;
}
}
int main()
{
cit();
solve();
return 0;
}