#include using namespace std; struct RNG { uint64_t x; explicit RNG(uint64_t seed = 0x243f6a8885a308d3ULL) : x(seed) {} uint64_t next() { uint64_t z = (x += 0x9e3779b97f4a7c15ULL); z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; return z ^ (z >> 31); } int uniform(int n) { return (int)(next() % (uint64_t)n); } }; void solveCase() { int N; cin >> N; const int V = N * N; vector val(V); vector row(N, 0), col(N, 0); int total = 0; for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { int a; cin >> a; int r = a % N; int id = i * N + j; val[id] = r; row[i] += r; if (row[i] >= N) row[i] -= N; col[j] += r; if (col[j] >= N) col[j] -= N; total += r; total %= N; } } if (total != 0) { cout << -1 << '\n'; return; } // おまけの簡単ケース: 全行が mod 0 なら行ごとに分ける bool okRows = true; for (int x : row) okRows &= (x == 0); if (okRows) { for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { if (j) cout << ' '; cout << i + 1; } cout << '\n'; } return; } // おまけの簡単ケース: 全列が mod 0 なら列ごとに分ける bool okCols = true; for (int x : col) okCols &= (x == 0); if (okCols) { for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { if (j) cout << ' '; cout << j + 1; } cout << '\n'; } return; } vector parent(V), order, st; vector acc(V), cutId(V), cellComp(V), ans(V); vector roots; order.reserve(V); st.reserve(V); roots.reserve(V); RNG rng(0x243f6a8885a308d3ULL ^ (uint64_t)N * 0x9e3779b97f4a7c15ULL); const int MAX_TRY = 30; for (int tc = 0; tc < MAX_TRY; ++tc) { fill(parent.begin(), parent.end(), -1); order.clear(); st.clear(); int root = rng.uniform(V); parent[root] = -2; order.push_back(root); st.push_back(root); // ランダム DFS で spanning tree を作る while (!st.empty()) { int v = st.back(); int i = v / N; int j = v - i * N; int cand[4]; int cnt = 0; int u; if (i > 0) { u = v - N; if (parent[u] == -1) cand[cnt++] = u; } if (i + 1 < N) { u = v + N; if (parent[u] == -1) cand[cnt++] = u; } if (j > 0) { u = v - 1; if (parent[u] == -1) cand[cnt++] = u; } if (j + 1 < N) { u = v + 1; if (parent[u] == -1) cand[cnt++] = u; } if (cnt == 0) { st.pop_back(); } else { u = cand[rng.uniform(cnt)]; parent[u] = v; order.push_back(u); st.push_back(u); } } // 葉側から mod N の和を見る。 // acc[v] == 0 なら、その場所で 1 部屋として切る。 acc = val; fill(cutId.begin(), cutId.end(), -1); roots.clear(); for (int t = V - 1; t >= 0; --t) { int v = order[t]; if (acc[v] == 0) { cutId[v] = (int)roots.size(); roots.push_back(v); } else { int p = parent[v]; if (p >= 0) { int x = acc[p] + acc[v]; if (x >= N) x -= N; acc[p] = x; } } } int M = (int)roots.size(); if (M < N) continue; // 各セルがどの cut component に属するかを上から決める。 vector compParent(M, -1), childCnt(M, 0); int rootComp = -1; for (int v : order) { int cid = cutId[v]; if (cid >= 0) { cellComp[v] = cid; int p = parent[v]; if (p >= 0) { int pc = cellComp[p]; compParent[cid] = pc; ++childCnt[pc]; } else { rootComp = cid; } } else { cellComp[v] = cellComp[parent[v]]; } } if (rootComp < 0) continue; // M 個の 0-mod component を、木上で葉から親にマージして N 個にする。 vector alive(M, 1); deque q; for (int c = 0; c < M; ++c) { if (c != rootComp && childCnt[c] == 0) { q.push_back(c); } } int aliveCnt = M; while (aliveCnt > N && !q.empty()) { int c = q.front(); q.pop_front(); if (!alive[c] || c == rootComp || childCnt[c] != 0) continue; alive[c] = 0; --aliveCnt; int p = compParent[c]; if (p >= 0) { --childCnt[p]; if (alive[p] && p != rootComp && childCnt[p] == 0) { q.push_back(p); } } } if (aliveCnt != N) continue; // alive な component を最終部屋番号 1..N にする。 vector rep(M, -1), finalId(M, 0); int idCnt = 0; for (int c = 0; c < M; ++c) { if (alive[c]) { rep[c] = c; finalId[c] = ++idCnt; } } for (int v = 0; v < V; ++v) { int c = cellComp[v]; int y = c; while (rep[y] < 0) y = compParent[y]; int r = rep[y]; y = c; while (rep[y] < 0) { int p = compParent[y]; rep[y] = r; y = p; } ans[v] = finalId[r]; } for (int i = 0; i < N; ++i) { for (int j = 0; j < N; ++j) { if (j) cout << ' '; cout << ans[i * N + j]; } cout << '\n'; } return; } // 解が存在してもここに来ることがある。 cout << -1 << '\n'; return; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) solveCase(); return 0; }