# include <stdio.h>
# include <vector>
# include <algorithm>
# include <map>
using namespace std;
#define MAXN 300
#define MAXQ 20000
#define COORD(i,j) (((i)-1)*n+(j))
long int a[MAXN+1][MAXN+1],n,q;
typedef struct REQUEST {long int x1,y1,x2,y2,sol;} REQUEST;
REQUEST req[MAXQ+1];
const long int vi[4] = {-1, 0, 1, 0};
const long int vj[4] = {0, 1, 0, -1};
map<long int, vector<long int> > reqNumbers;
typedef struct MOUNTAIN {long int x,y,h;} MOUNTAIN;
MOUNTAIN mountains[MAXN*MAXN+1];
long int multime[MAXN*MAXN+1];
vector<long int> merged;
long int getSet(long int coord){
long int getcoord = coord;
while (multime[getcoord] != getcoord){
getcoord = multime[getcoord];
}
multime[coord] = getcoord;
return getcoord;
}
void mergeSet(long int set1, long int set2, long int sol){
if (set1 == set2){
return;
}
multime[set1] = set2;
//
if (reqNumbers.find(set1) == reqNumbers.end()){
return;
}
if (reqNumbers.find(set2) == reqNumbers.end()){
reqNumbers[set2].swap(reqNumbers[set1]);
return;
}
long int i = 0;
long int j = 0;
merged.clear();
while (i < reqNumbers[set1].size() && j < reqNumbers[set2].size()){
if (reqNumbers[set1][i] == reqNumbers[set2][j]){
req[reqNumbers[set1][i]].sol = sol;
i++;
j++;
}
else if (reqNumbers[set1][i] < reqNumbers[set2][j]){
merged.push_back(reqNumbers[set1][i]);
i++;
} else {
merged.push_back(reqNumbers[set2][j]);
j++;
}
}
while (i < reqNumbers[set1].size()){
merged.push_back(reqNumbers[set1][i]);
i++;
}
while (j < reqNumbers[set2].size()){
merged.push_back(reqNumbers[set2][j]);
j++;
}
reqNumbers[set2].swap(merged);
reqNumbers.erase(set1);
}
void citire()
{
FILE * f = fopen ("matrice2.in","r");
fscanf(f,"%ld%ld",&n,&q);
for (long int i = 1; i <= n; i++)
for (long int j = 1; j <= n; j++){
fscanf(f,"%ld",&a[i][j]);
}
for (long int i = 1; i <= q; i++){
fscanf(f,"%ld%ld%ld%ld",&req[i].x1,&req[i].y1,&req[i].x2,&req[i].y2);
}
fclose(f);
}
bool compara(const MOUNTAIN& m1, const MOUNTAIN& m2)
{
return m2.h<m1.h;
}
void calculeaza()
{
//create the mountains
for (long int i = 1; i <= n; i++)
for (long int j = 1; j <= n; j++){
mountains[COORD(i,j)].x = i;
mountains[COORD(i,j)].y = j;
mountains[COORD(i,j)].h = a[i][j];
multime [COORD(i,j)] = COORD(i,j);
}
//attaching requests
for (long int i = 1; i <= q; i++){
reqNumbers[COORD(req[i].x1,req[i].y1)].push_back(i);
reqNumbers[COORD(req[i].x2,req[i].y2)].push_back(i);
}
//sorting by altitude
sort(mountains+1,mountains+n*n+1,compara);
//do the magic
for (long int i = 1; i <= n*n; i++){
//now checking mountains[i]. First, see if it has bigger newighbours
for (long int dir = 0; dir < 4; dir++){
long int nx = mountains[i].x + vi[dir];
long int ny = mountains[i].y + vj[dir];
if (nx >= 1 && nx <= n && ny >= 1 && ny <= n && a[nx][ny] > mountains[i].h){
//create a merge
//printf("%ld %ld with %ld %ld\n",mountains[i].x,mountains[i].y,nx,ny);fflush(stdin);getchar();
mergeSet(getSet(COORD(nx,ny)),getSet(COORD(mountains[i].x,mountains[i].y)), mountains[i].h);
}
}
}
}
void scrie()
{
FILE *g= fopen("matrice2.out","w");
for (long int i = 1; i <= q; i++)
fprintf(g,"%ld\n",req[i].sol);
fclose(g);
}
int main()
{
citire();
calculeaza();
scrie();
return 0;
}