#include <cstdio>
#include <cstring>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
const int nmax = 305;
const int qmax = 20005;
int n, q;
pair<int, int> a[nmax*nmax];
int ans[qmax];
struct query {
int x;
int y;
int i;
query(int x_, int y_, int i_) : x(x_), y(y_), i(i_) {}
query() : query(0,0,0) {}
inline bool operator<(const query& other) const {
return ans[i] > ans[other.i];
}
};
bool ok[nmax];
query Q[qmax];
int S[nmax*nmax];
vector<int> G[nmax*nmax];
inline int idx(int x, int y) {
return (x - 1) * n + (y - 1);
}
int F(int x) {
if (S[x] == x)
return x;
return S[x] = F(S[x]);
}
void U(int x, int y) {
int fx = F(x);
int fy = F(y);
if (fx == fy)
return;
S[fx] = fy;
}
int main() {
freopen("matrice2.in", "r", stdin);
freopen("matrice2.out", "w", stdout);
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; i++) {
for (int j = 1, x; j <= n; j++) {
scanf("%d", &x);
a[idx(i, j)] = make_pair(x, idx(i, j));
}
}
sort(a, a+n*n, [](const pair<int, int>& a, const pair<int, int>& b) {
return a.first > b.first;
});
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i < n) {
G[idx(i, j)].push_back(idx(i+1, j));
G[idx(i+1, j)].push_back(idx(i, j));
}
if (j < n) {
G[idx(i, j)].push_back(idx(i, j+1));
G[idx(i, j+1)].push_back(idx(i, j));
}
}
}
for (int i = 1, x1, y1, x2, y2; i <= q; i++) {
scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
Q[i] = query(idx(x1, y1), idx(x2, y2), i);
}
for (int k = 19; k >= 0; --k) {
for (int i = 0; i < n * n; i++) {
S[i] = i;
ok[i] = 0;
}
int potential = 1<<k;
int last = 0;
for (int i = 1; i <= q; i++) {
while (last < n * n && a[last].first >= ans[Q[i].i] + potential) {
for (int j = 0; j < G[a[last].second].size(); j++)
if (ok[G[a[last].second][j]])
U(a[last].second, G[a[last].second][j]);
ok[a[last].second] = 1;
last++;
}
if (F(Q[i].x) == F(Q[i].y)) {
ans[Q[i].i] += potential;
}
}
sort(Q+1, Q+q+1);
}
for (int i = 1; i <= q; i++) {
printf("%d\n", ans[i]);
}
return 0;
}