1. 程式人生 > 實用技巧 >題解 AT1974 【[ARC058B] いろはちゃんとマス目 / Iroha and a Grid】

題解 AT1974 【[ARC058B] いろはちゃんとマス目 / Iroha and a Grid】

傳送門

題意:

給出一個\(H \times W\)的矩陣,將左下角挖去一個\(A \times B\)的小矩陣,求從左上角走到右下角的方案數。
首先看好邊界問題,實際上只走了\(H + W - 2\)步。

思路:

這道題第一眼看上去有點容斥的意思,考慮怎麼求答案
如果沒有小矩陣,那麼方案數很顯然是 \(C_{H + W - 2}^{H - 1}\),但是有了小矩陣,那麼只需要把經過小矩陣的答案的貢獻減去就好了。
再考慮怎麼算經過小矩陣的方案數,首先暴力列舉一個位置\(i\)(小矩陣的上沿),即這條路線在該位置第一次經過小矩陣,此時一定是不合法的方案了,剩下的步數為\(W - i + A - 1\)

步(已經走了\(H - A + i - 1\)步)但是如果在走到\(i\)位置的時候如果繼續向右走,答案就會和\(i + 1\)的位置重複,所以我們強制要求到達\(i\)位置之後向下走一步,然後繼續走,當\(i\)為B的時候就不存在和\(i + 1\)重複的可能了,因此第\(B\)位置特殊處理,這樣就可以得到答案:
\(C_{H + W - 2}^{H - 1} - \sum\limits_{i = 1}^{B - 1} C_{H - A + i - 1}^{H - A} \times C_{W - i + A - 2}^{W - i} - C_{H - A + B - 1}^{H - A} \times C_{W - B + A - 1}^{W - B}\)

程式碼實現:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int maxn = 2e6 + 50, mod = 1e9 + 7;
long long H, W, A, B;
long long jc[maxn], ny[maxn], jcny[maxn];
void init () {
	jc[1] = ny[1] = jcny[1] = jcny[0] = 1;
	long long a, b;
	for (register long long i = 2; i <= maxn - 5; ++i) {
		jc[i] = jc[i - 1] * i % mod;
		a = mod / i, b = mod % i;
		ny[i] = (-a * ny[b] % mod + mod) % mod;
		jcny[i] = jcny[i - 1] * ny[i] % mod;
	}
}
inline long long C (int a, int b) {
	if (a == 0 || b == 0 || a == b) return 1;
	return jc[a] * jcny[b] % mod * jcny[a - b] % mod;
}
long long ans;
signed main () {
	init();
	scanf ("%lld%lld%lld%lld", &H, &W, &A, &B);
	ans = C(H + W - 2, H - 1);
	for (register int i = 1; i < B; i++) {
		ans = ((ans - (C(H - A + i - 1, H - A) * C(W - i + A - 2, W - i) % mod)) % mod + mod) % mod;
	}
	ans = ans = ((ans - (C(H - A + B - 1, H - A) * C(W- B + A - 1, W - B) % mod)) % mod + mod) % mod;
	cout<<ans<<endl;
	return 0;
}