CF559C:Gerald and Giant Chess 题解

主要思路

这一道题其实挺好的,我们来分析一下。

首先,我们知道在一个方格纸上,从左上角到右下角的路径的数量是\(C_{H+W-2}^{H-1}\),可以理解为横向路径的选法总数。之后,我们来进行容斥。在本题中,我们需要容斥掉那些经过了至少一次黑点的路径数量。因为本题中黑点个数小于\(5000\),所以我们直接来跑一个\(O(n^2)\)的 DP。设\(F[i]\)为经过了此点的路径数,然后我们把右下角的点设为第\(n+1\)个点,最后答案存在\(F[n+1]\)处。我们可以很快得到一个递推式:

\[ F[i] = C_{x_i + y_i – 2}^{x_i – 1} – \sum_{j = 1}^{i-1} (F[j] * C_{x_i-x_j+y_i-y_j}^{x_i-x_j}),且x_i \geq x_j, y_i \geq y_j. \]

解释:对于到点\(i\)的路径,首先不考虑之前的黑点则有\( C_{x_i + y_i – 2}^{x_i – 1} \)种路;而要容斥掉至少经过过一次的黑点的路径,可以直接对于之前的每一个点进行计算,计算之前的点\(j\)到现在的点\(i\)有多少种路径。

代码

// CF559C.cpp
#include <bits/stdc++.h>
#define ll long long
#define pr pair<ll, ll>
using namespace std;
const int MOD = 1e9 + 7, MAX_N = 2020, LEVEL_RG = 200000;
ll level[200010], inv[200010], n, h, w, F[MAX_N];
pr pts[MAX_N];
ll quick_power(ll bas, ll tim, ll mod)
{
    ll ans = 1;
    while (tim)
    {
        if (tim & 1)
            ans = ans * bas % mod;
        bas = bas * bas % mod;
        tim >>= 1;
    }
    return ans;
}
ll C(ll n, ll m) { return level[n] * inv[n - m] % MOD * inv[m] % MOD; }
int main()
{
    scanf("%d%d%d", &h, &w, &n);
    for (int i = 1; i <= n; i++)
        scanf("%d%d", &pts[i].first, &pts[i].second);
    sort(pts + 1, pts + 1 + n);
    pts[n + 1].first = h, pts[n + 1].second = w;
    level[0] = inv[0] = 1;
    for (int i = 1; i <= LEVEL_RG; i++)
        level[i] = level[i - 1] * i % MOD;
    ll inv_bas = quick_power(level[LEVEL_RG], MOD - 2, MOD);
    for (int i = LEVEL_RG; i >= 1; i--)
        inv[i] = inv_bas, inv_bas = inv_bas * i % MOD;
    for (int i = 1; i <= n + 1; i++)
    {
        F[i] = C(pts[i].first + pts[i].second - 2, pts[i].first - 1) % MOD;
        for (int j = 1; j < i; j++)
            if (pts[j].first <= pts[i].first && pts[j].second <= pts[i].second)
                F[i] = (F[i] - F[j] * C(pts[i].first + pts[i].second - pts[j].first - pts[j].second, pts[i].first - pts[j].first)) % MOD;
    }
    printf("%lld", (F[n + 1] + MOD) % MOD);
    return 0;
}

发布者

Kal0rona

江西师大附中全机房最弱

发表评论

电子邮件地址不会被公开。 必填项已用*标注