結果

問題 No.3742 Re: Verse X
コンテスト
ユーザー uruzunyaa
提出日時 2026-09-19 03:50:54
言語 C++23
(gcc 15.3.0 + boost 1.92.0 + ACL)
コンパイル:
g++-15 -O2 -lm -std=c++23 -Wuninitialized -DONLINE_JUDGE -o a.out _filename_
実行:
./a.out
結果
AC  
実行時間 50 ms / 2,000 ms
+ 718µs
コード長 12,458 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 4,038 ms
コンパイル使用メモリ 367,420 KB
実行使用メモリ 11,996 KB
最終ジャッジ日時 2026-09-19 13:26:18
合計ジャッジ時間 7,926 ms
ジャッジサーバーID
(参考情報)
judge2_0 / judge3_0
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 3
other AC * 57
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#include <bits/stdc++.h>
using namespace std;

struct Op {
    int k, r, c; // r,c は 0-indexed
};

// 左上付近の「2層目の (1,2) だけ」を反転する
const vector<Op> Q_TOP = {
    {3,3,4},
    {1,1,6},
    {1,6,1}
};

// 左上付近の「2層目の (2,1) だけ」を反転する
const vector<Op> Q_LEFT = {
    {3,4,3},
    {1,1,6},
    {1,6,1}
};

// 最外周の (0,0),(0,2),(2,0) を反転する
const vector<Op> 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<Op> 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<Op> P2 = {
    {1,1,5},
    {1,1,7},
    {1,2,4},
    {3,3,5}
};

// 最外周の (0,3),(0,5) を反転する
const vector<Op> 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<string> S(N);
    for (auto& s : S) cin >> s;

    vector<vector<int>> a(N, vector<int>(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<pair<int,int>> 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<Op> ops;
        vector<bitset<B>> 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<B> 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<bitset<B>, B> basis_v;
        array<bitset<B>, B> basis_c;
        array<bool, B> has{};

        for (int i = 0; i < M; i++) {
            bitset<B> v = vecs[i];
            bitset<B> 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<B> 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<B> 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<Op> 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<vector<array<unsigned char, 4>>> used(
        N,
        vector<array<unsigned char, 4>>(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<Op>& g,
                                bool flip_r,
                                bool flip_c,
                                bool transpose) {
        vector<Op> res;

        for (auto op : g) {
            res.push_back(
                transform_op(op, flip_r, flip_c, transpose)
            );
        }

        return res;
    };

    auto apply_gadget = [&](const vector<Op>& g) {
        for (auto op : g) {
            apply_op(op);
        }
    };

    // ============================================================
    // 1. 外から2層目を全部白にする
    // ============================================================

    // 角付近の例外8マス
    set<pair<int,int>> 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<Op>{
                {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<SideTransform> 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<int,int>{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<Op> 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';
    }
}
0