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;
}
投稿日時:
最終更新:
