H - Akari Counting 解説 by harurun4635


個人的に楽に感じた解法を記しておきます。公式解説における \(E,F,G,H\) を先に決定する方法です。公式解説をある程度前提としています。


\(E, F, G, H\) 内の照明を決め打つ

\(E,F,G,H\) の範囲内の照明」の個数を \(p\) 個とします。 これは、\(h_1, h_3\) から合計 \(p\) 行、\(w_1, w_3\) から合計 \(p\) 列選んだあとに、それらをマッチングすればよいです。

照明が \(A, B, C, D\) のみで問題を解く

\(E,F,G,H\) の範囲内の照明」によって照らされる行・列を取り除くと、元問題と同じ形で「 \(A,B,C,D\) にしか明かりがない」という状況の問題を解けばよいことになります。以下ではこの問題を考えます。

ここで、\(A\) の領域のマスは「 \(A\) に置かれた照明」でしか照らされません。よって \(\min (h_1, w_2)\) 個の照明が置かれます。

簡易的な証明 一般性を失わず $A$ を $h \le w$ の $h \times w$ グリッドと考えましょう。
「 $h$ より多く置く」と、同じ行に $2$ つの照明が配置されてしまうので不可能です。逆に、「 $h$ より少なく置く」と、照明の存在しない行がありますが、その行について列側の寄与を考えても高々 $h-1$ マスしか照らされません。 $h-1 \lt w$ ですから、不可能です。

では、この条件が満たされるときに \(E, F, G, H\) のすべてのマスが照らされる条件を考えましょう。 \(A\) において「 \(\min (h_1, w_2) = h_1\) 」であるとき \(A\) は「良い領域」と呼ぶことにします。( \(B, C, D\) でも適切に回転して定義します) \(E, F, G, H\) のすべてについて「それに隣接する \(2\) つの領域のうち、少なくとも \(1\) つが「良い領域」である」ことが必要十分条件です。

簡易的な証明 どちらも「良い領域」ではないとき、明らかに「照らされない行」と「照らされない列」が存在してしまいます。この双方に含まれるマスは照らされません。 逆にどちらかが「良い領域」であれば、それによってすべてのマスが照らされます。

数え上げる

それぞれが「良い領域」であるかどうかは \(2^4\) 通りですから、これらをすべてについて条件を満たすか確かめればよいです。

完全に行と列を分けて考えることができることに注意すれば、簡単な畳み込みで記述できます。

以下はcppの簡単な擬似コードです。 (二項係数の前計算などを省略しています)

vector<vll> calc(int h, int w){
    vll ok(h + 1, 0); // 良い領域
    vll ng(h + 1, 0); // 良くない領域
    rep(i, 0, h + 1){
        int r = h - i;
        if (r <= w) ok[i] = comb.C(h, i) * comb.P(w, r);
        else ng[i] = comb.C(h, i) * comb.P(r, w);
    }
    return {ok, ng};
}

int main(){
    int h, w, a, b, c, d; cin >> h >> w >> a >> b >> c >> d;
    int h1 = a-1, h2 = b-a+1, h3 = h-b;
    int w1 = c-1, w2 = c-d+1, w3 = w-d;
    auto va = calc(h1, w2);
    auto vb = calc(w1, h2);
    auto vc = calc(h3, w2);
    auto vd = calc(w3, h2);
    vector<vll> row, col;
    rep(f, 0, 2) rep(g, 0, 2){
        row.pb(convolution(va[f], vc[g]));
        col.pb(convolution(vb[f], vd[g]));
    }
    int ma = min(h1 + h3, w1 + w3);
    ll ans = 0;
    rep(f, 0, 4) rep(g, 0, 4){
        if (f == 0 or g == 0){
            rep(p, 0, ma + 1){
                ans += row[f][p] * col[g][p] % mod * comb.F(p) % mod;
                ans %= mod;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

投稿日時:
最終更新: