/// arbori de intervale pe 2 dimensiuni
#include <iostream>
#include <cstdio>
using namespace std;
const int NMAX = 500;
struct poz {
int i, j;
poz(int _i = 0, int _j = 0) : i(_i), j(_j) {}
};
int n, m;
int a[NMAX + 5][NMAX + 5];
int aint[NMAX * NMAX + 5];
int init(int nod, poz ss, poz dj) {
if(ss.i > dj.i || ss.j > dj.j)
return -1;
if(ss.i == dj.i && ss.j == dj.j) {
aint[nod] = a[ss.i][ss.j];
return aint[nod];
}
int mij_i = (dj.i + ss.i) / 2, mij_j = (dj.j + ss.j) / 2;
aint[nod] = max(aint[nod], init(4 * nod + 1, ss, poz(mij_i, mij_j)));
aint[nod] = max(aint[nod], init(4 * nod + 2, poz(ss.i, mij_j + 1), poz(mij_i, dj.j)));
aint[nod] = max(aint[nod], init(4 * nod + 3, poz(mij_i + 1, ss.j), poz(dj.i, mij_j)));
aint[nod] = max(aint[nod], init(4 * nod + 4, poz(mij_i + 1, mij_j + 1), dj));
return aint[nod];
}
int query(int nod, poz ss, poz dj, poz qss, poz qdj) {
if(ss.i > dj.i || ss.j > dj.j || ss.i > qdj.i || ss.j > qdj.j || dj.i < qss.i || dj.j < qss.j)
return -1;
if(ss.i >= qss.i && ss.j >= qss.j && dj.i <= qdj.i && dj.j <= qdj.j)
return aint[nod];
int mij_i = (dj.i + ss.i) / 2, mij_j = (dj.j + ss.j) / 2;
int q1 = query(4 * nod + 1, ss, poz(mij_i, mij_j), qss, qdj);
int q2 = query(4 * nod + 2, poz(ss.i, mij_j + 1), poz(mij_i, dj.j), qss, qdj);
int q3 = query(4 * nod + 3, poz(mij_i + 1, ss.j), poz(dj.i, mij_j), qss, qdj);
int q4 = query(4 * nod + 4, poz(mij_i + 1, mij_j + 1), dj, qss, qdj);
return max(max(q1, q2), max(q3, q4));
}
int main() {
freopen("plantatie.in", "r", stdin);
freopen("plantatie.out", "w", stdout);
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
scanf("%d", &a[i][j]);
init(0, poz(1, 1), poz(n, n));
int x, y, z;
for(int i = 1; i <= m; i++) {
scanf("%d %d %d", &x, &y, &z);
printf("%d\n", query(0, poz(1, 1), poz(n, n), poz(x, y), poz(x + z - 1, y + z - 1)));
}
return 0;
}