Official

D - Domino Covering XOR Editorial by MMNMM


合計 \(n\) マスの(長方形とは限らない)マス目に対するありうるドミノの置き方の総数の最大値を \(f(n)\) と書くことにし、\(f(n)\) の増加の度合いについて考えます。

\(n\) マスのマス目のうち、左にも上にもマス目が存在しないマスを \(1\) つ取ります。 このマス目には、

  • ドミノが置かれない
  • 縦向きのドミノが置かれる
  • 横向きのドミノが置かれる

のいずれか \(1\) つが成り立ちます。 それぞれの場合に対するドミノの置き方の総数はそれぞれ高々 \(f(n-1),f(n-2),f(n-2)\) 通りです。

よって、\[f(n)\leq f(n-1)+2f(n-2)\] が成り立ちます。\(f(1)=1,f(0)=1\) より、\(f(n)\leq\dfrac{2 ^ {n+1}+(-1) ^ n}3\) がいえます。

よって、ドミノの置き方すべてを探索しても十分高速であることがわかります。 すでにドミノが置かれたマスを \(HW\operatorname{bit}\) の整数で管理すると実装が簡単になります。

実装例は以下のようになります。 時間計算量は \(O(HW2 ^ {HW})\) などとなります。 \(30\) 行目の直後で重複を取り除くことで管理する状態の数を減らすことができますが、重複を取り除くための処理で計算量が悪化する場合があることに注意してください。

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    using namespace std;
    unsigned H, W;
    cin >> H >> W;
    vector A(H, vector<unsigned long>(W));
    for (auto&& row : A)
        for (auto&& a : row)
            cin >> a;

    // HW ビットの整数でドミノが置かれたマスを管理する
    vector<unsigned> possible_domino{0}; // はじめは何も置かれていない盤面だけ

    const unsigned domino_vertical = (1U << W) + 1, domino_horizontal = 3;

    for (unsigned i = 0, bit = 0; i < H; ++i)
        for (unsigned j = 0; j < W; ++j, ++bit) { // 各マスについて
            vector<unsigned> tmp;
            for (const auto b : possible_domino) {
                // 縦に置けるなら置いたものを追加する
                if (j + 1 < W && !(b & domino_horizontal << bit))
                    tmp.emplace_back(b | domino_horizontal << bit);
                // 横に置けるなら置いたものを追加する
                if (i + 1 < H && !(b & domino_vertical << bit))
                    tmp.emplace_back(b | domino_vertical << bit);
            }
            ranges::move(tmp, back_inserter(possible_domino));
        }

    unsigned long ans = 0;
    for (const auto b : possible_domino) { // 各ドミノの置き方について
        unsigned long now = 0;
        for (unsigned i = 0, bit = 0; i < H; ++i)
            for (unsigned j = 0; j < W; ++j, ++bit)
                if (1 & ~b >> bit) // ドミノが置かれてないマスに
                    now ^= A[i][j]; // 書かれた整数の xor を求める
        ans = max(ans, now); // 大きければ更新
    }

    cout << ans << endl;
    return 0;
}

posted:
last update: