# 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;
long int reqsol[MAXQ+1];
const long int vi[4] = {-1, 0, 1, 0};
const long int vj[4] = {0, 1, 0, -1};
vector<long int>* reqNumbers[MAXN*MAXN+1];
typedef struct MOUNTAIN {long int x,y;} 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;
//
long int i = 0;
long int j = 0;
if (reqNumbers[set1] == NULL){
return;
}
if (reqNumbers[set2] == NULL){
reqNumbers[set2] = reqNumbers[set1];
reqNumbers[set1] = NULL;
return;
}
vector<long int>& reqn1 = *reqNumbers[set1];
vector<long int>& reqn2 = *reqNumbers[set2];
merged.clear();
while (i < reqn1.size() && j < reqn2.size()){
if (reqn1[i] == reqn2[j]){
reqsol[reqn1[i]] = sol;
i++;
j++;
}
else if (reqn1[i] < reqn2[j]){
merged.push_back(reqn1[i]);
i++;
} else {
merged.push_back(reqn2[j]);
j++;
}
}
while (i < reqn1.size()){
merged.push_back(reqn1[i]);
i++;
}
while (j < reqn2.size()){
merged.push_back(reqn2[j]);
j++;
}
reqn2.swap(merged);
delete reqNumbers[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]);
}
long int x1,y1,x2,y2;
for (long int i = 1; i <= q; i++){
fscanf(f,"%ld%ld%ld%ld",&x1,&y1,&x2,&y2);
if (reqNumbers[COORD(x1,y1)] == NULL){
reqNumbers[COORD(x1,y1)] = new vector<long int>();
}
reqNumbers[COORD(x1,y1)]->push_back(i);
if (reqNumbers[COORD(x2,y2)] == NULL){
reqNumbers[COORD(x2,y2)] = new vector<long int>();
}
reqNumbers[COORD(x2,y2)]->push_back(i);
}
fclose(f);
}
bool compara(const MOUNTAIN& m1, const MOUNTAIN& m2)
{
return a[m2.x][m2.y]<a[m1.x][m1.y];
}
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;
multime [COORD(i,j)] = COORD(i,j);
}
//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] > a[mountains[i].x][mountains[i].y]){
//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)), a[mountains[i].x][mountains[i].y]);
}
}
}
}
void scrie()
{
FILE *g= fopen("matrice2.out","w");
for (long int i = 1; i <= q; i++)
fprintf(g,"%ld\n",reqsol[i]);
fclose(g);
}
int main()
{
citire();
calculeaza();
scrie();
return 0;
}