cf559 C. Gerald and Giant Chess(dp+组合数)


题意:

n*m网格,其中有k个网格中有障碍物不能走。每步只能往下或往右求从左上走到右下的路径数。

\(1\le n,m\le 1e5,1\le k \le 2000\)

思路:

网格总数很大,但是障碍物很少,显然复杂度跟障碍数有关。

一开始想的是容斥:所有路径数 - 经过至少一个障碍的路径数 + 经过至少两个障碍的路径数 - …… ,然后就晕了

正解:对所有障碍物排序,\(dp(i)\) 表示从左上走到第 \(i\) 个障碍物,中间不经过任何一个障碍物的路径数

怎么计算 \(dp(i)\) 呢?可以把从左上走到第 \(i\) 个障碍物的路径这样分类:不经过任何一个障碍物的、路径上第一个障碍物的序号为1、路径上的第一个障碍物为2、……

那么 \(dp(i)=C_{x_i-1+y_i-1}^{x_i-1}-\sum\limits _{会影响到i的j} dp(j)*C_{x_i-x_j+y_i-y_j}^{x_i-x_j}\)

为方便,增加一个位于终点的障碍物。

ll del(ll x, ll y) { return x-y<0?x-y+MOD:x-y; }

main()
{
    iofast;
    cin >> n >> m >> k;
    for(int i = 1; i <= k; i++) cin >> a[i].fi >> a[i].se;

    init(); //预处理阶乘和逆元
    sort(a + 1, a + 1 + k);
    a[++k] = {n,m};

    for(int i = 1; i <= k; i++)
    {
        ll x = a[i].fi, y = a[i].se;
        for(int j = 1; j < i; j++)
            if(a[j].fi <= x && a[j].se <= y)
                (dp[i] += dp[j] * C(x-a[j].fi+y-a[j].se,x-a[j].fi) % MOD) %= MOD;
        dp[i] = del( C(x-1+y-1,x-1) , dp[i]);
    }
    cout << dp[k];
}