#include <bits/stdc++.h>
using namespace std;
const int N = 810;
struct SNode {
int l;
int r;
int max;
int min;
int getmid() {
return l + r >> 1;
}
};
struct tree {
int l;
int r;
SNode t[N << 2];
int getmid() {
return l + r >> 1;
}
}t[N << 2];
int n, m, a[N][N];
void buildY(int l, int r, int p2, int p, int flag)//flag = 1作区间更新,否则仅赋值
{
t[p].t[p2].l = l;
t[p].t[p2].r = r;
if(l == r)
{
if(flag) {
t[p].t[p2].max = max(t[p * 2].t[p2].max, t[p * 2 + 1].t[p2].max);
t[p].t[p2].min = min(t[p * 2].t[p2].min, t[p * 2 + 1].t[p2].min);
}
else
t[p].t[p2].min = t[p].t[p2].max = a[t[p].l][l];
return;
}
int mid = l + r >> 1;
buildY(l, mid, p2 * 2, p, flag);
buildY(mid + 1, r, p2 * 2 + 1, p, flag);
t[p].t[p2].max = max(t[p].t[p2 * 2].max,t[p].t[p2 * 2 + 1].max);
t[p].t[p2].min = min(t[p].t[p2 * 2].min,t[p].t[p2 * 2 + 1].min);
}
void buildX(int l, int r, int p2)
{
t[p2].l = l;
t[p2].r = r;
if(l == r)
{
buildY(1, n, 1, p2, 0);
return;
}
int mid = t[p2].getmid();
buildX(l, mid, p2 * 2);
buildX(mid + 1, r, p2 * 2 + 1);
buildY(1, n, 1, p2, 1);
}
int QueryMaxY(int l, int r, int p2, int p)
{
if(t[p].t[p2].l == l && t[p].t[p2].r == r)
return t[p].t[p2].max;
int mid = t[p].t[p2].getmid();
if(r <= mid)
return QueryMaxY(l, r, p2 * 2, p);
else if(l > mid)
return QueryMaxY(l, r, p2 * 2 + 1, p);
else
return max(QueryMaxY(l, mid, p2 * 2, p), QueryMaxY(mid + 1, r, p2 * 2 + 1, p));
}
int QueryMaxX(int l, int r, int x, int y, int p2)
{
if(t[p2].l == l && t[p2].r == r)
return QueryMaxY(x, y, 1, p2);
int mid = (t[p2].l + t[p2].r) >> 1;
if(r <= mid)
return QueryMaxX(l, r, x, y, p2 * 2);
else if(l > mid)
return QueryMaxX(l, r, x, y, p2 * 2 + 1);
else
return max(QueryMaxX(l, mid, x, y, p2 * 2), QueryMaxX(mid + 1, r, x, y, p2 * 2 + 1));
}
int QueryMinY(int l, int r, int p2, int p)
{
if(t[p2].l == l&&t[p2].r == r)
return t[p].t[p2].min;
int mid = (t[p].t[p2].l + t[p].t[p2].r) >> 1;
if(r <= mid)
return QueryMinY(l, r, p2 * 2, p);
else if(l > mid)
return QueryMinY(l, r, p2 * 2 + 1, p);
else
return min(QueryMinY(l, mid, p2 * 2, p), QueryMinY(mid + 1, r, p2 * 2 + 1, p));
}
int QueryMinX(int l,int r,int x,int y,int p2)
{
if(t[p2].l == l && t[p2].r == r)
return QueryMinY(x, y, 1, p2);
int mid = (t[p2].l + t[p2].r) >> 1;
if(r <= mid)
return QueryMinX(l, r, x, y, p2 * 2);
else if(l > mid)
return QueryMinX(l, r, x, y, p2 * 2 + 1);
else
return min(QueryMinX(l, mid, x, y, p2 * 2), QueryMinX(mid + 1, r, x, y, p2 * 2 + 1));
}
void changeY(int y, int p2, int val, int p, int flag)
{
if(t[p].t[p2].l == t[p].t[p2].r)
{
if(!flag)
t[p].t[p2].min = t[p].t[p2].max = val;
else
{
t[p].t[p2].min = min(t[p * 2].t[p2].min, t[p * 2 + 1].t[p2].min);
t[p].t[p2].max = max(t[p * 2].t[p2].max, t[p * 2 + 1].t[p2].max);
}
return;
}
int mid = (t[p].t[p2].l + t[p].t[p2].r) >> 1;
if(y <= mid)
changeY(y, p2 * 2, val, p, flag);
else
changeY(y, p2 * 2 + 1, val, p, flag);
t[p].t[p2].max = max(t[p].t[p2 * 2].max, t[p].t[p2 * 2 + 1].max);
t[p].t[p2].min = min(t[p].t[p2 * 2].min, t[p].t[p2 * 2 + 1].min);
}
void changeX(int x, int y, int p2, int val)
{
if(t[p2].l == t[p2].r)
{
changeY(y, 1, val, p2, 0);
return;
}
int mid = (t[p2].l + t[p2].r) >> 1;
if(x <= mid)
changeX(x, y, p2 * 2, val);
else
changeX(x, y, p2 * 2 + 1,val);
changeY(y, 1, val, p2, 1);
}
int main()
{
int T;
cin >> T;
while(T--)
{
cin >> n;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
cin >> a[i][j];
buildX(1, n, 1);
cin >> m;
while(m--)
{
int x, y, l;
cin >> x >> y >> l;
int sx = max(1, x - l / 2);
int sy = max(1, y - l / 2);
int mx = min(n, x + l / 2);
int my = min(n, y + l / 2);
int max = QueryMaxX(sx, mx, sy, my, 1);
int min = QueryMinX(sx, mx, sy, my, 1);
cout << (max + min) / 2;
changeX(x, y, 1, (max + min) >> 1);
}
}
return 0;
}