#include <bits/stdc++.h>
using namespace std;
int n,nrq,hmax,Next;
int mat[303][303];
vector<pair<int,int> >nod;
vector<int>G[303*303];
int ans[20003];
int comp[303*303];
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
struct query{
int a,b,poz;
};
vector<query>Q;
bool inside(int x,int y){
return x>=1 && y>=1 && x<=n && y<=n;
}
int poz(int x,int y){
return (x-1)*n+y;
}
void Reinit(){
Next=0;
for(int i=1;i<=n*n;i++){
comp[i]=i;
}
}
int Find(int x){
int root=x;
while(root!=comp[root]){
root=comp[root];
}
while(x!=root){
int aux=comp[x];
comp[x]=root;
x=aux;
}
return root;
}
void add(int x){
for(auto y:G[x]){
comp[Find(x)]=Find(y);
}
}
int main()
{
freopen("matrice2.in","r",stdin);
freopen("matrice2.out","w",stdout);
scanf("%d %d",&n,&nrq);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&mat[i][j]);
hmax=max(hmax,mat[i][j]);
comp[poz(i,j)]=poz(i,j);
nod.push_back({mat[i][j],poz(i,j)});
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int dir=0;dir<4;dir++){
int newx=i+dx[dir];
int newy=j+dy[dir];
if(inside(newx,newy) && mat[newx][newy]>=mat[i][j]){
G[poz(i,j)].push_back(poz(newx,newy));
}
}
}
}
for(int i=1;i<=nrq;i++){
int a,b,x,y;
scanf("%d %d %d %d",&a,&b,&x,&y);
Q.push_back({poz(a,b),poz(x,y),i});
}
sort(nod.rbegin(),nod.rend());
queue<pair<pair<int,int>,vector<query> > >q;
q.push( { {1,hmax},Q });
Next=0;
while(!q.empty()){
int l=q.front().first.first;
int r=q.front().first.second;
vector<query>Left;
vector<query>Right;
vector<query>act=q.front().second;
int mid=(l+r)/2;
if(nod[Next].first<mid){
Reinit();
}
while(nod[Next].first>mid){
add(nod[Next].second);
Next++;
}
for(auto x:act){
if(Find(x.a)==Find(x.b)){
Left.push_back(x);
}else{
Right.push_back(x);
}
}
if(r-l<2){
for(auto x:Left){
ans[x.poz]=r;
}
for(auto x:Right){
ans[x.poz]=l;
}
}else{
if(Left.size()>0){
q.push( { {mid+1,r},Left } );
}
if(Right.size()>0){
q.push( { {l,mid},Right } );
}
}
q.pop();
}
for(int i=1;i<=nrq;i++)
printf("%d\n",ans[i]);
return 0;
}