题目链接:http://arc058.contest.atcoder.jp/tasks/arc058_b
题目
有一个 的矩形区域。Iroha从左上角 走到右下角 ,每次只能向右或者向下走一步,并且Iroha不能经过左下角区域 的矩形区域。问Iroha有多少种走法mod (1e9+7)。
思考过程:
- 首先如果没有限制条件,从
走到
的方案数为
或者
。
- 证明:走的步数一定是 步,然后你在这里选择向下的都是第几步,那么向右的步数就确定下来,而且向下和向右都是有序的(即只可能有一种排序状态)。(我的理解,可能不太好)
- 我原来想的是总的走的方案数减掉经过A * B区域的方案数,后来发现不会算……然后我就尝试着将整个矩形分成A * B上方和右方两块矩形。
(1,1) ... (1,B) -> | (1,B+1)
(2,1) ... (2,B) -> | (2,B+1)
(3,1) ... (3,B) -> | (3,B+1)
... ... ... -> | ...
(H-A,1) ... (H-A,B)-> | (H-A,B+1)
|
|
A * B |
| (H,W)
3.左边矩形枚举它最后达到的点(1 ~ H-A, B),然后从这个点向右走一步走到右边区域,然后再以右边那个点为起点走到终点。可以看出来这样的结果相加是没有重合的。
4.推出公式:
推2个极端情况:
5.如何算
?
- 打表?数组100000 * 100000 很明显不可以啊。
- 所以直接算:注意我前面的组合数有一个特点,就是左边的组合数的下面那个数都是B - 1,而右边组合数下面那个数都是W - B - 1,这样可以根据
推出来……
6.最后一个问题,这里有个模,而算组合数时有个除号,所以这里要求
对
的逆元,这里打表即可。
代码
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long llong;
const llong mod = 1e9 + 7;
const int maxn = 300000 + 5;
llong c1[maxn];
llong c2[maxn];
llong inv[maxn];
void Prepare_inv()
{
inv[1] = 1;
for (llong i = 2; i < maxn; i++)
{
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
//printf("inv[%lld]=%lld\n", i, inv[i]);
}
}
void C1(llong n, llong r)
{
c1[r] = 1;
for (llong i = r + 1; i <= n; i++)
{
c1[i] = c1[i - 1] * i % mod * inv[i - r] % mod;
//printf("c1[%lld][%lld]=%lld\n", i, r, c1[i]);
}
}
void C2(llong n, llong r)
{
c2[r] = 1;
for (llong i = r + 1; i <= n; i++)
{
c2[i] = c2[i - 1] * i % mod * inv[i - r] % mod;
//printf("c2[%lld][%lld]=%lld\n", i, r, c2[i]);
}
}
int main()
{
Prepare_inv();
llong h, w, a, b;
while (scanf("%lld %lld %lld %lld", &h, &w, &a, &b) != EOF)
{
if (h <= a || w <= b)
{
puts("0");
continue;
}
C1(h - a + b - 2, b - 1);
C2(w - b + h - 2, w - b - 1);
llong ans = 0;
llong right = 0;
llong left = 0;
for (llong i = h - a + b - 2; i >= b - 1; i--)
{
left = c1[i];
right = c2[h + w - 3 - i];
ans = (ans + left * right % mod) % mod;
//printf("i=%lld j=%lld %lld %lld %lld\n", i, h+w-3-i, left, right, ans);
}
printf("%lld\n", ans);
}
return 0;
}