Official

E - Grid Filling Editorial by MMNMM


この問題は、 \(0\leq k\leq H,0\leq l\leq W,0\leq x\lt N\) について

  • \(\operatorname{count}[k][l][x]:=\) \(k\lt i\leq H,l\lt j\leq W\) を満たすマス \((i,j)\) のうち、\(x\) が書かれているマスの個数

が計算できていれば \(O(HWN)\) 時間で解くことができます。 実際、\((k+1,l+1)\) から \((k+h,l+w)\) までを塗りつぶしたとき、塗りつぶされていないマスのうち \(x\) が書かれているマスの個数は

\[\operatorname{count}[0][0][x]-\operatorname{count}[k][l][x]+\operatorname{count}[k][l+w][x]-\operatorname{count}[k+h][l+w][x]+\operatorname{count}[k+h][l][x]\]

個であるため、\((k,l)\) に対する答えを \(O(N)\) 時間で求めることができます。

\(\operatorname{count}[k][l][x]\) を求めるには、二次元累積和を使えばよいです。

具体的な方法を以下に述べます。

  • \(\operatorname{count}[k][l][x]=\) \((k+1,j+1)\) に \(x\) が書かれているとき \(1\) 、そうでないとき \(0\)

とすることは \(O(HWN)\) 時間で可能です。 \((k,j,x)\) を適切な順序でわたって \(\operatorname{count}[k][l][x]\leftarrow\operatorname{count}[k][l][x]+\operatorname{count}[k][l+1][x]\) と更新することで、

  • \(\operatorname{count}[k][l][x]=\) \(k\lt l\leq W\) を満たすマス \((k+1,l)\) のうち、\(x\) が書かれているマスの個数

が成り立っているようにできます。 同様に \(\operatorname{count}[k][l][x]\leftarrow\operatorname{count}[k][l][x]+\operatorname{count}[k+1][l][x]\) と更新することで、欲しい \(\operatorname{count}\) の値が得られます。

以上より、全体で \(O(HWN)\) 時間でこの問題を解くことができました。

実装例は以下のようになります。

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

int main() {
    using namespace std;

    unsigned H, W, N, h, w;
    cin >> H >> W >> N >> h >> w;

    // count[i][j][x] = マス (i + 1, j + 1) に x が書き込まれているとき 1, そうでないとき 0
    vector count(H, vector(W, vector<unsigned>(N)));

    for (auto &&row : count) {
        for (auto &&grid : row) {
            unsigned long A;
            cin >> A;
            ++grid[A - 1];
        }
        // 番兵
        row.emplace_back(N);
    }
    // 番兵
    count.emplace_back(W + 1, vector<unsigned>(N));

    // 二次元累積和
    std::inclusive_scan(rbegin(count), rend(count), rbegin(count), [](auto &&i, auto &&j) {
        const auto vector_plus{[](auto &&i, auto &&j) {
            vector<unsigned> ret;
            transform(begin(i), end(i), begin(j), back_inserter(ret), plus<>{});
            return ret;
        }};
        std::inclusive_scan(rbegin(j), rend(j), rbegin(j), vector_plus);
        vector<vector<unsigned>> ret;
        transform(begin(i), end(i), begin(j), back_inserter(ret), vector_plus);
        return ret;
    });

    // board[i][j][x] = マス (i, j) より右下のマスに x が書き込まれている個数

    for (unsigned i{}; i + h <= H; ++i) {
        for (unsigned j{}; j + w <= W; ++j) {
            unsigned ans{};
            // 塗りつぶされていないマスに x と書いてある個数が 1 以上のとき、答えを 1 増やす
            for (unsigned x{}; x < N; ++x)
                ans += count[0][0][x] + count[i + h][j][x] + count[i][j + w][x] !=
                       count[i][j][x] + count[i + h][j + w][x];
            cout << ans << " ";
        }
        cout << endl;
    }

    return 0;
}

posted:
last update: