#include <bits/stdc++.h>
using namespace std;
const int NMAX = 301;
const int QMAX = 20001;
const int MAX = 1000001;
struct Query
{
int x1, y1, x2, y2;
int left, right, mid, answer;
bool solved;
};
struct Value
{
int val, i, j;
};
int N, Q, M;
int a[NMAX][NMAX];
Query q[QMAX];
bool found[QMAX];
Value vals[NMAX * NMAX];
int root[NMAX * NMAX];
bool dsu[NMAX][NMAX];
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
int diff[NMAX * NMAX];
vector <int> ind[NMAX * NMAX];
map <int, int> valtopos;
bool Inside(int i, int j)
{
return 1 <= i && i <= N && 1 <= j && j <= N;
}
int Convert(int i, int j)
{
return (i - 1) * N + j;
}
void Union(int x, int y)
{
if (rand() % 2)
root[y] = x;
else
root[x] = y;
}
int Find(int x)
{
int r = x, cx;
while (root[r] != 0)
r = root[r];
while (x != r)
{
cx = root[x];
root[x] = r;
x = cx;
}
return r;
}
int main()
{
srand(time(NULL));
ifstream fin("matrice2.in");
ofstream fout("matrice2.out");
fin >> N >> Q;
for (int i = 1;i <= N;++i)
for (int j = 1;j <= N;++j)
{
fin >> a[i][j];
vals[(i - 1) * N + j] = {a[i][j], i, j};
diff[(i - 1) * N + j] = a[i][j];
}
sort(diff + 1, diff + N * N + 1);
for (int i = 1, jumpi = 1;i <= N * N;i = jumpi)
{
while (jumpi <= N * N && diff[jumpi] == diff[i])
++jumpi;
diff[++M] = diff[i];
valtopos[diff[i]] = M;
}
sort(vals + 1, vals + N * N + 1, [&](Value x, Value y)
{
return x.val < y.val;
});
for (int i = 1;i <= Q;++i)
{
int x1, y1, x2, y2;
fin >> x1 >> y1 >> x2 >> y2;
q[i] = {x1, y1, x2, y2, 1, M, M / 2, 0};
}
int solved = 0;
while (solved < Q)
{
solved = 0;
for (int i = 1;i <= Q;++i)
{
q[i].mid = (q[i].left + q[i].right) / 2;
ind[q[i].mid].push_back(i);
}
for (int i = N * N;i >= 1;--i)
{
int x, y, a, b;
for (int j = 0;j < 4;++j)
{
x = vals[i].i + dx[j];
y = vals[i].j + dy[j];
if (Inside(x, y) && dsu[x][y] == true)
{
a = Find(Convert(vals[i].i, vals[i].j));
b = Find(Convert(x, y));
if (a != b)
Union(a, b);
}
}
if (vals[i].val != vals[i - 1].val)
{
int aux = valtopos[vals[i].val];
for (auto& j : ind[aux])
{
Query qq = q[j];
a = Find(Convert(qq.x1, qq.y1));
b = Find(Convert(qq.x2, qq.y2));
if (a == b)
found[j] = true;
else
found[j] = false;
}
}
dsu[vals[i].i][vals[i].j] = true;
}
for (int i = 1;i <= Q;++i)
{
if (found[i] == true)
{
q[i].left = q[i].mid + 1;
q[i].answer = diff[q[i].mid];
}
else
q[i].right = q[i].mid - 1;
if (q[i].right < q[i].left)
++solved;
found[i] = false;
}
for (int i = 1;i <= N;++i)
for (int j = 1;j <= N;++j)
dsu[i][j] = false;
for (int i = 1;i <= N * N;++i)
{
root[i] = 0;
ind[i].clear();
}
}
for (int i = 1;i <= Q;++i)
fout << q[i].answer << "\n";
fin.close();
fout.close();
return 0;
}