1. 程式人生 > 其它 >cf559 C. Gerald and Giant Chess(dp+組合數)

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