#include <bits/stdc++.h>
#define MaxN 303
#define MaxQ 20003
using namespace std;
struct query {
int a, b, poz;
};
int n, nrq, hmax, Next;
int mat[MaxN][MaxN];
pair<int, int> nod[MaxN * MaxN];
vector<int> G[MaxN * MaxN];
int ans[MaxQ];
int comp[MaxN * MaxN];
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
queue<pair<pair<int, int>, vector<query>>> q;
vector<query> Q;
inline bool inside(int x, int y) {
return x >= 1 && y >= 1 && x <= n && y <= n;
}
inline int poz(int x, int y) { return (x - 1) * n + y; }
inline void Reinit() {
Next = 1;
for (int i = 1; i <= n * n; i++) {
comp[i] = i;
}
}
inline 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;
}
inline void add(int x) {
for (int i = 0; i < G[x].size(); i++) {
comp[Find(x)] = Find(G[x][i]);
}
}
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]);
nod[poz(i, j)] = {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 + 1, nod + 1 + n * n);
reverse(nod + 1, nod + 1 + n * n);
Reinit();
q.push({{1, hmax}, Q});
Next = 1;
while (!q.empty()) {
int l = q.front().first.second;
int r = q.front().first.first;
vector<query> Left;
vector<query> Right;
int mid = (l + r) / 2;
if (nod[Next].first < mid) {
Reinit();
}
while (nod[Next].first > mid) {
add(nod[Next].second);
Next++;
}
for (int i = 0; i < q.front().second.size(); i++) {
query x = q.front().second[i];
if (Find(x.a) == Find(x.b)) {
Left.push_back(x);
} else {
Right.push_back(x);
}
}
if (l - r < 2) {
for (auto x : Left) {
ans[x.poz] = l;
}
for (auto x : Right) {
ans[x.poz] = r;
}
} else {
if (!Left.empty()) {
q.push({{mid + 1, l}, Left});
}
if (!Right.empty()) {
q.push({{r, mid}, Right});
}
}
q.pop();
}
for (int i = 1; i <= nrq; i++)
printf("%d\n", ans[i]);
return 0;
}