#include <bits/stdc++.h>
#pragma GCC optimize("O2")
std::ifstream fin("plantatie.in");
std::ofstream fout("plantatie.out");
/*
RMQ 3D
-> interogare de forma (i, j, lat) <=> Care este maximum din submatricea cu coltul stanga sus (i, j) si latura lat
Implementation:a
-> precalculare pentru fiecare (i, j) calculam maximele pe toate submatricele cu coltul stanga sus(i, j) si latura putere de 2
-> trebuie sa folosim un tablou 3D de dimensiune n * n * log(n)
*/
const int size = 5e2 + 5;
const int log_size = 10;
int n, q;
int a[size][size];
struct SparseTable {
int exponent[log_size];
int spt[log_size][size][size]; // spt[e][i][j] = max in (i, j) cu latura 2^e
void init(int n)
{
exponent[1] = 0;
for(int i = 2; i <= n; ++i)
exponent[i] = 1 + exponent[i / 2];
// spt[0][i][j] = max in (i, j) cu latura 1, adica a[i][j]
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
spt[0][i][j] = a[i][j];
// spt[p][i][j] = max in (i, j) cu latura 2^p, putem sa ne folosim de spt[p - 1]
// folosim lat ca sa ne asiguram ca ramanem in matrice => AM FOST APROAPE CORECT
for(int p = 1, lat = 2; p <= exponent[n]; ++p, lat *= 2)
for(int i = 1; i + lat - 1 <= n; ++i)
for(int j = 1; j + lat - 1 <= n; ++j)
{
int x = i + lat / 2;
int y = j + lat / 2;
spt[p][i][j] = std::max(
std::max(spt[p - 1][i][j], spt[p - 1][x][y]),
std::max(spt[p - 1][x][j], spt[p - 1][i][y])
);
}
}
int query(int xs, int ys, int L)
{
int lg2 = exponent[L];
int len = (1 << lg2);
int xm = xs + L - len;
int ym = ys + L - len;
// presupun ca se face cam la fel, insa acum facem pentru X-si, dar si pentru Y-ci
// ne gandim la o compresie cu D&C in 4 submatrici
return std::max(
std::max(spt[lg2][xs][ys], spt[lg2][xm][ys]),
std::max(spt[lg2][xs][ym], spt[lg2][xm][ym])
);
}
} rmq;
int main()
{
std::ios_base::sync_with_stdio(false);
fin.tie(nullptr);
fout.tie(nullptr);
fin >> n >> q;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
fin >> a[i][j];
rmq.init(n);
while(q--)
{
int xs, ys, len;
fin >> xs >> ys >> len;
fout << rmq.query(xs, ys, len) << "\n";
}
return 0;
}