#include using namespace std; struct Op { int k, r, c; // r,c は 0-indexed }; // 左上付近の「2層目の (1,2) だけ」を反転する const vector Q_TOP = { {3,3,4}, {1,1,6}, {1,6,1} }; // 左上付近の「2層目の (2,1) だけ」を反転する const vector Q_LEFT = { {3,4,3}, {1,1,6}, {1,6,1} }; // 最外周の (0,0),(0,2),(2,0) を反転する const vector CORNER = { {1,1,3}, {1,1,5}, {1,2,2}, {1,3,1}, {1,5,1}, {3,3,3} }; // 最外周の (0,1),(0,3) を反転する const vector P1 = { {1,1,4}, {1,1,6}, {1,2,1}, {1,2,5}, {1,4,1}, {1,5,2}, {1,6,1}, {3,3,4}, {3,4,3} }; // 最外周の (0,2),(0,4) を反転する const vector P2 = { {1,1,5}, {1,1,7}, {1,2,4}, {3,3,5} }; // 最外周の (0,3),(0,5) を反転する const vector P3 = { {1,1,2}, {1,1,6}, {1,3,2}, {1,5,2}, {3,3,4} }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N; cin >> N; vector S(N); for (auto& s : S) cin >> s; vector> a(N, vector(N)); for (int r = 0; r < N; r++) { for (int c = 0; c < N; c++) { a[r][c] = (S[r][c] == '#'); } } auto cells_of = [&](const Op& op) { vector> cells; for (int d = -op.k; d <= op.k; d++) { cells.push_back({op.r + d, op.c + d}); if (d != 0) { cells.push_back({op.r + d, op.c - d}); } } return cells; }; // ============================================================ // N <= 10 // 小さいので盤面全体を GF(2) ガウス消去 // ============================================================ if (N <= 10) { constexpr int B = 128; vector ops; vector> vecs; for (int k = 1; 2 * k + 1 <= N; k++) { for (int r = k; r + k < N; r++) { for (int c = k; c + k < N; c++) { Op op{k, r, c}; bitset v; for (auto [x, y] : cells_of(op)) { v.set(x * N + y); } ops.push_back(op); vecs.push_back(v); } } } int D = N * N; int M = ops.size(); array, B> basis_v; array, B> basis_c; array has{}; for (int i = 0; i < M; i++) { bitset v = vecs[i]; bitset c; c.set(i); for (int p = 0; p < D; p++) { if (!v[p]) continue; if (has[p]) { v ^= basis_v[p]; c ^= basis_c[p]; } else { has[p] = true; basis_v[p] = v; basis_c[p] = c; break; } } } bitset target; for (int r = 0; r < N; r++) { for (int c = 0; c < N; c++) { if (a[r][c]) { target.set(r * N + c); } } } bitset comb; for (int p = 0; p < D; p++) { if (!target[p]) continue; if (!has[p]) { cout << -1 << '\n'; return 0; } target ^= basis_v[p]; comb ^= basis_c[p]; } vector ans; for (int i = 0; i < M; i++) { if (comb[i]) { ans.push_back(ops[i]); } } cout << ans.size() << '\n'; for (auto [k, r, c] : ans) { cout << k << ' ' << r + 1 << ' ' << c + 1 << '\n'; } return 0; } // ============================================================ // N >= 11 // ============================================================ // 4辺 × 市松模様2色 = 8個の不変量 for (int p = 0; p < 2; p++) { int x = 0; // 上辺 for (int c = p; c < N; c += 2) { x ^= a[0][c]; } if (x) { cout << -1 << '\n'; return 0; } // 下辺 x = 0; for (int c = p; c < N; c += 2) { x ^= a[N - 1][c]; } if (x) { cout << -1 << '\n'; return 0; } // 左辺 x = 0; for (int r = p; r < N; r += 2) { x ^= a[r][0]; } if (x) { cout << -1 << '\n'; return 0; } // 右辺 x = 0; for (int r = p; r < N; r += 2) { x ^= a[r][N - 1]; } if (x) { cout << -1 << '\n'; return 0; } } // 同じ操作を2回行ったら打ち消し合うので、 // 各操作を使う回数の偶奇だけ管理する。 vector>> used( N, vector>(N) ); auto apply_op = [&](const Op& op) { used[op.r][op.c][op.k] ^= 1; for (int d = -op.k; d <= op.k; d++) { a[op.r + d][op.c + d] ^= 1; if (d != 0) { a[op.r + d][op.c - d] ^= 1; } } }; auto transform_op = [&](Op op, bool flip_r, bool flip_c, bool transpose) { if (transpose) { swap(op.r, op.c); } if (flip_r) { op.r = N - 1 - op.r; } if (flip_c) { op.c = N - 1 - op.c; } return op; }; auto transform_gadget = [&](const vector& g, bool flip_r, bool flip_c, bool transpose) { vector res; for (auto op : g) { res.push_back( transform_op(op, flip_r, flip_c, transpose) ); } return res; }; auto apply_gadget = [&](const vector& g) { for (auto op : g) { apply_op(op); } }; // ============================================================ // 1. 外から2層目を全部白にする // ============================================================ // 角付近の例外8マス set> special; for (int fr = 0; fr < 2; fr++) { for (int fc = 0; fc < 2; fc++) { int r1 = fr ? N - 2 : 1; int c1 = fc ? N - 3 : 2; special.insert({r1, c1}); int r2 = fr ? N - 3 : 2; int c2 = fc ? N - 2 : 1; special.insert({r2, c2}); } } // 例外以外の2層目は、中心に k=1 を置けば // 2層目の中ではそのマスだけが反転する。 for (int r = 0; r < N; r++) { for (int c = 0; c < N; c++) { int d = min({ r, c, N - 1 - r, N - 1 - c }); if (d != 1) continue; if (special.count({r, c})) continue; if (a[r][c]) { apply_op({1, r, c}); } } } // 角付近の例外8マスを消す for (int fr = 0; fr < 2; fr++) { for (int fc = 0; fc < 2; fc++) { int r1 = fr ? N - 2 : 1; int c1 = fc ? N - 3 : 2; if (a[r1][c1]) { apply_gadget( transform_gadget( Q_TOP, fr, fc, false ) ); } int r2 = fr ? N - 3 : 2; int c2 = fc ? N - 2 : 1; if (a[r2][c2]) { apply_gadget( transform_gadget( Q_LEFT, fr, fc, false ) ); } } } // ============================================================ // 2. 最外周の四隅を白にする // ============================================================ for (int fr = 0; fr < 2; fr++) { for (int fc = 0; fc < 2; fc++) { int r = fr ? N - 1 : 0; int c = fc ? N - 1 : 0; if (a[r][c]) { apply_gadget( transform_gadget( CORNER, fr, fc, false ) ); } } } // 上辺の (0,j),(0,j+2) だけを反転する gadget auto top_pair_gadget = [&](int j) { if (j == 1) { return P1; } if (j == 2) { return P2; } if (j == 3) { return P3; } if (4 <= j && j <= N - 7) { return vector{ {3,3,j + 1}, {1,1,j - 1}, {1,1,j + 3} }; } if (j == N - 6) { return transform_gadget( P3, false, true, false ); } if (j == N - 5) { return transform_gadget( P2, false, true, false ); } return transform_gadget( P1, false, true, false ); }; struct SideTransform { bool flip_r; bool flip_c; bool transpose; }; vector sides = { // 上 {false, false, false}, // 下 {true, false, false}, // 左 {false, false, true}, // 右 {false, true, true} }; auto transform_point = [&](int r, int c, SideTransform t) { if (t.transpose) { swap(r, c); } if (t.flip_r) { r = N - 1 - r; } if (t.flip_c) { c = N - 1 - c; } return pair{r, c}; }; // ============================================================ // 3. 最外周を2マスずつ右へ送る要領で消す // ============================================================ for (auto st : sides) { for (int j = 1; j <= N - 4; j++) { auto [r, c] = transform_point(0, j, st); if (!a[r][c]) continue; auto g = top_pair_gadget(j); apply_gadget( transform_gadget( g, st.flip_r, st.flip_c, st.transpose ) ); } } // ============================================================ // 4. 残った内部を1マス反転 gadget で消す // // X_2(r,c) // xor X_1(r-1,c-1) // xor X_1(r-1,c+1) // xor X_1(r+1,c-1) // xor X_1(r+1,c+1) // // で (r,c) だけを反転する。 // ============================================================ for (int r = 2; r <= N - 3; r++) { for (int c = 2; c <= N - 3; c++) { if (!a[r][c]) continue; apply_op({2, r, c}); apply_op({1, r - 1, c - 1}); apply_op({1, r - 1, c + 1}); apply_op({1, r + 1, c - 1}); apply_op({1, r + 1, c + 1}); } } // ============================================================ // 同じ操作を偶数回使ったものを相殺して出力 // ============================================================ vector ans; for (int k = 1; k <= 3; k++) { for (int r = 0; r < N; r++) { for (int c = 0; c < N; c++) { if (used[r][c][k]) { ans.push_back({k, r, c}); } } } } cout << ans.size() << '\n'; for (auto [k, r, c] : ans) { cout << k << ' ' << r + 1 << ' ' << c + 1 << '\n'; } }