#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define INF 2140000000
#define MaxN 305
#define MaxQ 200000
#define pi 3.1415926535897932384626433832795
using namespace std;
FILE *IN, *OUT;
int N, Q, Last[MaxQ], Max = 0, pos = 0;
struct spec
{
int fx, fy, value;
} Mat[MaxN][MaxN];
struct spec2
{
int value, x, y;
} l[MaxN * MaxN];
struct query
{
int x1, y1, x2, y2, pos;
};
vector<query> Quest;
bool cond(spec2 a, spec2 b)
{
return a.value > b.value;
}
queue<pair<pair<int, int>, vector<query>>> Queue;
void Reinitialize()
{
pos = 0;
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
Mat[i][j].fx = i, Mat[i][j].fy = j;
}
pair<int, int> find(int x, int y)
{
pair<int, int> orig;
orig.first = x, orig.second = y;
while (Mat[x][y].fx != x || Mat[x][y].fy != y)
{
int ax = Mat[x][y].fx, ay = Mat[x][y].fy;
x = ax, y = ay;
}
while (orig.first != x || orig.second != y)
{
int ax = Mat[orig.first][orig.second].fx,
ay = Mat[orig.first][orig.second].fy;
Mat[orig.first][orig.second].fx = x;
Mat[orig.first][orig.second].fy = y;
orig.first = ax, orig.second = ay;
}
return make_pair(x, y);
}
void Add(spec2 point)
{
int X = point.x, Y = point.y, V = point.value;
if (Mat[X][Y].fx != X || Mat[X][Y].fy != Y)
return;
if (X > 1 && Mat[X - 1][Y].value >= V)
{
pair<int, int> f = find(X - 1, Y);
Mat[f.first][f.second].fx = X;
Mat[f.first][f.second].fy = Y;
}
if (Y > 1 && Mat[X][Y - 1].value >= V)
{
pair<int, int> f = find(X, Y - 1);
Mat[f.first][f.second].fx = X;
Mat[f.first][f.second].fy = Y;
}
if (X < N && Mat[X + 1][Y].value >= V)
{
pair<int, int> f = find(X + 1, Y);
Mat[f.first][f.second].fx = X;
Mat[f.first][f.second].fy = Y;
}
if (Y < N && Mat[X][Y + 1].value >= V)
{
pair<int, int> f = find(X, Y + 1);
Mat[f.first][f.second].fx = X;
Mat[f.first][f.second].fy = Y;
}
}
int main() {
IN = fopen("matrice2.in", "r");
OUT = fopen("matrice2.out", "w");
fscanf(IN, "%d%d", &N, &Q);
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
{
fscanf(IN, "%d", &Mat[i][j].value);
Mat[i][j].fx = i;
Mat[i][j].fy = j;
l[(i - 1) * N + j].value = Mat[i][j].value;
l[(i - 1) * N + j].x = i;
l[(i - 1) * N + j].y = j;
Max = max(Max, Mat[i][j].value);
}
sort(l + 1, l + 1 + N * N, cond);
for (int i = 1; i <= Q; i++)
{
query aux;
fscanf(IN, "%d%d%d%d", &aux.x1, &aux.y1, &aux.x2, &aux.y2);
aux.pos = i;
Quest.push_back(aux);
}
l[0].value = INF;
Queue.push(make_pair(make_pair(Max, 0), Quest));
while (!Queue.empty())
{
int L = Queue.front().first.first;
int R = Queue.front().first.second;
int Mid = (R + L) >> 1;
vector<query> left, right;
if (l[pos].value < Mid)
Reinitialize();
while (l[pos + 1].value > Mid)
Add(l[++pos]);
for (int i = 0; i < Queue.front().second.size(); i++)
{
query aux = Queue.front().second[i];
if (find(aux.x1, aux.y1) == find(aux.x2, aux.y2))
left.push_back(aux);
else
right.push_back(aux);
}
if (L - R <= 1)
{
for (int i = 0; i < left.size(); i++)
Last[left[i].pos] = L;
for (int i = 0; i < right.size(); i++)
Last[right[i].pos] = R;
}
else
{
if (!left.empty())
Queue.push(make_pair(make_pair(L, Mid + 1), left));
if (!right.empty())
Queue.push(make_pair(make_pair(Mid, R), right));
}
Queue.pop();
}
for (int i = 1; i <= Q; i++)
fprintf(OUT, "%d\n", Last[i]);
return 0;
}