题目链接:http://arc058.contest.atcoder.jp/tasks/arc058_b
题目
有一个 H ∗ W H * W H∗W的矩形区域。Iroha从左上角 ( 1 , 1 ) (1,1) (1,1)走到右下角 ( H , W ) (H, W) (H,W),每次只能向右或者向下走一步,并且Iroha不能经过左下角区域 A ∗ B A * B A∗B的矩形区域。问Iroha有多少种走法mod (1e9+7)。
- 1 ≤ H , W ≤ 100 , 000 1\leq H,W \leq 100,000 1≤H,W≤100,000
- 1 ≤ A < H 1\leq A < H 1≤A<H
- 1 ≤ B < W 1\leq B <W 1≤B<W
思考过程:
- 首先如果没有限制条件,从 ( x 1 , y 1 ) (x_1, y_1) (x1,y1)走到 ( x 2 , y 2 ) (x_2, y_2) (x2,y2)的方案数为 ( x 2 − x 1 + y 2 − y 1 x 2 − x 1 ) \binom{x_2 - x_1 + y_2 - y_1 }{x_2-x_1} (x2−x1x2−x1+y2−y1)或者 ( x 2 − x 1 + y 2 − y 1 y 2 − y 1 ) \binom{x_2 - x_1 + y_2 - y_1 }{y_2-y_1} (y2−y1x2−x1+y2−y1)。
- 证明:走的步数一定是 x 2 − x 1 + y 2 − y 1 x_2 - x_1 + y_2 - y_1 x2−x1+y2−y1步,然后你在这里选择向下的都是第几步,那么向右的步数就确定下来,而且向下和向右都是有序的(即只可能有一种排序状态)。(我的理解,可能不太好)
- 我原来想的是总的走的方案数减掉经过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个极端情况:
(
1
,
1
)
−
>
(
H
−
A
,
B
)
:
(
H
−
A
−
1
+
B
−
1
B
−
1
)
(1,1)->(H-A,B):\binom{H-A-1+B-1}{B-1}
(1,1)−>(H−A,B):(B−1H−A−1+B−1)
(
H
−
A
,
B
+
1
)
−
>
(
H
,
W
)
:
(
A
+
W
−
B
−
1
W
−
B
−
1
)
(H-A,B+1)->(H,W):\binom{A+W-B-1}{W-B-1}
(H−A,B+1)−>(H,W):(W−B−1A+W−B−1)
(
1
,
1
)
−
>
(
1
,
B
)
:
(
B
−
1
B
−
1
)
(1,1)->(1,B):\binom{B-1}{B-1}
(1,1)−>(1,B):(B−1B−1)
(
1
,
B
+
1
)
−
>
(
H
,
W
)
:
(
H
−
1
+
W
−
B
−
1
W
−
B
−
1
)
(1,B+1)->(H,W):\binom{H-1+W-B-1}{W-B-1}
(1,B+1)−>(H,W):(W−B−1H−1+W−B−1)
(
H
−
A
+
B
−
2
B
−
1
)
(
W
−
B
+
H
−
2
W
−
B
−
1
)
+
.
.
.
+
(
B
−
1
B
−
1
)
(
H
−
1
+
W
−
B
−
1
W
−
B
−
1
)
\binom{H-A+B-2}{B-1}\binom{W - B + H - 2}{W - B - 1}+...+\binom{B-1}{B-1}\binom{H-1+W-B-1}{W-B-1}
(B−1H−A+B−2)(W−B−1W−B+H−2)+...+(B−1B−1)(W−B−1H−1+W−B−1)
5.如何算 ( x y ) \binom{x}{y} (yx)?
- 打表?数组100000 * 100000 很明显不可以啊。
- 所以直接算:注意我前面的组合数有一个特点,就是左边的组合数的下面那个数都是B - 1,而右边组合数下面那个数都是W - B - 1,这样可以根据
(
n
r
)
=
(
n
−
1
r
)
∗
n
n
−
r
\binom{n}{r}=\frac{\binom{n-1}{r}*n}{n-r}
(rn)=n−r(rn−1)∗n推出来……
6.最后一个问题,这里有个模,而算组合数时有个除号,所以这里要求 ( n − r ) (n-r) (n−r)对 m o d mod mod的逆元,这里打表即可。
代码
#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;
}