AtCoder Regular Contest 058 D

题目链接:http://arc058.contest.atcoder.jp/tasks/arc058_b

题目

有一个 H W 的矩形区域。Iroha从左上角 ( 1 , 1 ) 走到右下角 ( H , W ) ,每次只能向右或者向下走一步,并且Iroha不能经过左下角区域 A B 的矩形区域。问Iroha有多少种走法mod (1e9+7)。

  • 1 H , W 100 , 000
  • 1 A < H
  • 1 B < W

思考过程:

  1. 首先如果没有限制条件,从 x 1 , y 1 走到 x 2 , y 2 的方案数为 ( x 2 x 1 + y 2 y 1 x 2 x 1 ) 或者 ( x 2 x 1 + y 2 y 1 y 2 y 1 )
    • 证明:走的步数一定是 x 2 x 1 + y 2 y 1 步,然后你在这里选择向下的都是第几步,那么向右的步数就确定下来,而且向下和向右都是有序的(即只可能有一种排序状态)。(我的理解,可能不太好)
  2. 我原来想的是总的走的方案数减掉经过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 )
( H A , B + 1 ) > ( H , W ) : ( A + W B 1 W B 1 )

( 1 , 1 ) > ( 1 , B ) : ( B 1 B 1 )
( 1 , B + 1 ) > ( H , W ) : ( H 1 + W B 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 )

5.如何算 ( x y ) ?
- 打表?数组100000 * 100000 很明显不可以啊。
- 所以直接算:注意我前面的组合数有一个特点,就是左边的组合数的下面那个数都是B - 1,而右边组合数下面那个数都是W - B - 1,这样可以根据 ( n r ) = ( n 1 r ) n n r 推出来……
6.最后一个问题,这里有个模,而算组合数时有个除号,所以这里要求 ( n r ) m o d 的逆元,这里打表即可。

代码

#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;
}

猜你喜欢

转载自blog.csdn.net/qq_35414878/article/details/80048997