#include <bits/stdc++.h>
using namespace std;
const long long mod = 1e9 + 7;
const int MaxN = 2e5 + 5;
const int BlockSize = 447;
int r, s, q, mx;
vector<vector<int>> arr;
void Inp()
{
cin >> r >> s;
mx = 0;
arr.resize(r + 1);
arr[0].resize(s + 1);
for (int x = 1; x <= r; x++)
{
arr[x].resize(s + 1);
for (int y = 1; y <= s; y++)
{
cin >> arr[x][y];
mx = max(mx, arr[x][y]);
}
}
}
int Hash(int x, int y)
{
return (x - 1) * s + y;
}
bool CheckX(int a)
{
return 1 <= a && a <= r;
}
bool CheckY(int a)
{
return 1 <= a && a <= s;
}
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
vector<vector<bool>> visited;
vector<pair<int, int>> tmpnow;
void DFS(int a, int b)
{
visited[a][b] = true;
tmpnow.push_back(make_pair(a, b));
for (int x = 0; x < 4; x++)
{
int newa = a + dx[x], newb = b + dy[x];
if (CheckX(newa) && CheckY(newb) && !visited[newa][newb] && arr[newa][newb] == arr[a][b])
{
DFS(newa, newb);
}
}
}
int curBig;
int parents[MaxN];
int isbig[MaxN];
int curcolor[MaxN];
vector<pair<int, int>> lst[MaxN];
vector<int> mrkbig[MaxN / BlockSize + 5][MaxN / 2];
vector<int> mrksmall[MaxN];
int FindSet(int p)
{
if (parents[p] == p)
{
return p;
}
parents[p] = FindSet(parents[p]);
return parents[p];
}
bool tmp[MaxN];
bool tmp2[MaxN];
void UnionSet(int a, int b)
{
a = FindSet(a);
b = FindSet(b);
if (a == b)
{
return;
}
if ((int)lst[a].size() < (int)lst[b].size())
{
swap(a, b);
}
bool isa = !isbig[a], isb = !isbig[b];
if (isa && isb)
{
int ua = isbig[a], ub = isbig[b];
for (int x = 0; x <= mx; x++)
{
for (int y : mrkbig[ua][x])
{
tmp[y] = true;
}
for (int y : mrkbig[ub][x])
{
if (!tmp[y])
{
tmp[y] = true;
mrkbig[ua][x].push_back(y);
}
}
for (int y : mrkbig[ua][x])
{
tmp[y] = false;
}
mrkbig[ub][x].clear();
}
}
else if (isb)
{
for (pair<int, int> x : lst[b])
{
for (int t = 0; t < 4; t++)
{
int newa = x.first + dx[t], newb = x.second + dy[t];
if (CheckX(newa) && CheckY(newb))
{
int newu = FindSet(Hash(newa, newb));
if (newu != a && newu != b)
{
mrkbig[isbig[a]][curcolor[newu]].push_back(newu);
}
}
}
}
}
parents[b] = a;
for (pair<int, int> x : lst[b])
{
lst[a].push_back(x);
}
lst[b].clear();
for (int x = 0; x < (int)mrksmall[a].size(); x++)
{
if (mrksmall[a][x] == b)
{
swap(mrksmall[a][x], mrksmall[a].back());
mrksmall[a].pop_back();
x--;
}
}
for (int x : mrksmall[a])
{
tmp[x] = true;
}
for (int x : mrksmall[b])
{
if (!tmp[x] && x != a)
{
mrksmall[a].push_back(x);
tmp[x] = true;
}
}
for (int x : mrksmall[a])
{
tmp[x] = false;
}
}
void ChangeBig(int u, int c)
{
curcolor[u] = c;
for (int x : mrkbig[isbig[u]][c])
{
if (curcolor[FindSet(x)] == c && (int)lst[FindSet(x)].size() <= BlockSize)
{
UnionSet(u, x);
}
}
mrkbig[isbig[u]][c].clear();
vector<int> out;
for (int x : mrksmall[u])
{
if (curcolor[FindSet(x)] == c)
{
out.push_back(x);
}
}
for (int x : out)
{
UnionSet(u, x);
}
}
void ChangeSmall(int u, int c)
{
curcolor[u] = c;
vector<int> out, plc;
for (pair<int, int> x : lst[u])
{
for (int y = 0; y < 4; y++)
{
int newa = x.first + dx[y], newb = x.second + dy[y];
if (CheckX(newa) && CheckY(newb))
{
int newu = FindSet(Hash(newa, newb));
if (tmp[newu])
{
continue;
}
if (u != newu && curcolor[newu] == c)
{
tmp[newu] = true;
out.push_back(newu);
}
else if (isbig[newu])
{
tmp[newu] = true;
plc.push_back(newu);
}
}
}
}
for (int x : out)
{
UnionSet(u, x);
}
u = FindSet(u);
if ((int)lst[u].size() > BlockSize)
{
curBig++;
isbig[u] = curBig;
vector<int> out;
for (pair<int, int> x : lst[u])
{
for (int y = 0; y < 4; y++)
{
int newa = x.first + dx[y], newb = x.second + dy[y];
if (CheckX(newa) && CheckY(newb) && FindSet(Hash(newa, newb)) != u)
{
int newu = FindSet(Hash(newa, newb));
if (tmp2[newu])
{
continue;
}
tmp2[newu] = true;
out.push_back(newu);
if ((int)lst[newu].size() <= BlockSize)
{
mrkbig[isbig[u]][curcolor[newu]].push_back(newu);
}
else
{
mrksmall[u].push_back(newu);
mrksmall[newu].push_back(u);
}
}
}
}
for (int x : out)
{
tmp2[x] = false;
}
}
else
{
for (int x : plc)
{
mrkbig[isbig[x]][c].push_back(u);
}
}
for (int x : out)
{
tmp[x] = false;
}
for (int x : plc)
{
tmp[x] = false;
}
}
void Exc()
{
visited.resize(r + 1);
for (int x = 0; x <= r; x++)
{
visited[x].resize(s + 1);
}
curBig = 0;
for (int x = 1; x <= r; x++)
{
for (int y = 1; y <= s; y++)
{
if (!visited[x][y])
{
tmpnow.clear();
DFS(x, y);
int u = Hash(x, y);
for (pair<int, int> z : tmpnow)
{
parents[Hash(z.first, z.second)] = u;
lst[u].push_back(z);
}
curcolor[u] = arr[x][y];
if ((int)tmpnow.size() > BlockSize)
{
curBig++;
isbig[u] = curBig;
}
}
}
}
for (int x = 1; x <= r; x++)
{
for (int y = 1; y <= s; y++)
{
if (parents[Hash(x, y)] == Hash(x, y))
{
int u = Hash(x, y);
set<int> out;
for (pair<int, int> z : lst[u])
{
for (int t = 0; t < 4; t++)
{
int newa = z.first + dx[t], newb = z.second + dy[t];
if (CheckX(newa) && CheckY(newb) && FindSet(Hash(newa, newb)) != u)
{
out.insert(FindSet(Hash(newa, newb)));
}
}
}
if ((int)lst[u].size() <= BlockSize)
{
for (int x : out)
{
if ((int)lst[x].size() > BlockSize)
{
mrksmall[u].push_back(x);
}
}
}
else
{
for (int x : out)
{
if ((int)lst[x].size() <= BlockSize)
{
mrkbig[isbig[u]][curcolor[x]].push_back(x);
}
else
{
mrksmall[u].push_back(x);
}
}
}
}
}
}
cin >> q;
for (int x = 1; x <= q; x++)
{
int a, b, c;
cin >> a >> b >> c;
int u = Hash(a, b);
u = FindSet(u);
if (isbig[u])
{
ChangeBig(u, c);
}
else
{
ChangeSmall(u, c);
}
}
for (int x = 1; x <= r; x++)
{
for (int y = 1; y <= s; y++)
{
cout << curcolor[FindSet(Hash(x, y))] << " ";
}
cout << "\n";
}
}
int main()
{
//freopen("PAINT.INP", "r", stdin);
//freopen("PAINT.OUT", "w", stdout);
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int test = 1;
//cin >> test;
for (int x = 1; x <= test; x++)
{
Inp();
Exc();
}
return 0;
}