CF559C Gerald and Giant Chess

组合数学 + 容斥

首先考虑一下不经过障碍物的情况,从$(1, 1)$到$(n, m)$相当于在$n +m - 2$步中选出$m - 1$步往下走,方案数$\binom{n + m - 2}{m - 1}$。

再考虑一下只有一个障碍物的情况,假设这个障碍物$(x, y)$,那么总的方案数是$\binom{n + m - 2}{m - 1} - \binom{n + m - x - y}{m - y} * \binom{x + y - 2}{y - 1}$。

那么有多个障碍物的情况怎么办呢?

考虑到一个障碍物$(x, y)$只会对$(1, 1) ~ (x, y)$之内的格子产生影响,所以我们可以考虑从容斥,这样子就得到一个类似于dp的东西。

我脑子一抽就写了个反的

设$f_{i}$表示从$(n, m)$走到第$i$个障碍物的方案数,那么有

  $f_{i} = \binom{n + m - x - y}{m - y}$ 

  $f_{i} -= f_{j} * \binom{x' - x + y' - y}{y' - y}$   $(j != i), x \leq x', y \leq y'$(假设$i$的坐标是$(x, y)$,$j$的坐标是$(x', y')$)。

把所有障碍物排序一遍就很好写了。

组合数可以$O(n)$预处理。

时间复杂度$(n^{2})$。

Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N = 2005;
const int M = 2e5 + 5;
const ll P = 1e9 + 7;

int n, m, K;
ll fac[M], inv[M], f[N];

struct Node {
    int x, y;
} a[N];

bool cmp(const Node &u, const Node &v) {
    if(u.x == v.x) return u.y < v.y;
    else return u.x < v.x;
}

inline void read(int &X) {
    X = 0; char ch = 0; int op = 1;
    for(; ch > '9' || ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

inline ll pow(ll x, ll y) {
    ll res = 1LL;
    for(; y > 0; y >>= 1) {
        if(y & 1) res = res * x % P;
        x = x * x % P;
    }
    return res;
}

inline ll getC(int x, int y) {
    return fac[x] * inv[y] % P * inv[x - y] % P;
}

int main() {
    read(n), read(m), read(K);
    
    for(int i = fac[0] = 1; i <= n + m; i++) 
        fac[i] = 1LL * fac[i - 1] * i % P;
    inv[n + m] = pow(fac[n + m], P - 2);
    for(int i = n + m - 1; i >= 0; i--)
        inv[i] = 1LL * inv[i + 1] * (i + 1) % P;
    
//    printf("%lld\n", getC(7, 3));
    
    for(int i = 1; i <= K; i++) 
        read(a[i].x), read(a[i].y);
//    a[++K].x = 1, a[K].y = 1;
    sort(a + 1, a + 1 + K, cmp);
      
    ll ans = getC(m + n - 2, m - 1);  
    for(int i = K; i >= 1; i--) {
        f[i] = (f[i] + getC(m + n - a[i].x - a[i].y, m - a[i].y) + P) % P;
        for(int j = i + 1; j <= K; j++) 
            if(a[j].x >= a[i].x && a[j].y >= a[i].y)
                f[i] = (f[i] - f[j] * getC(a[j].x - a[i].x + a[j].y - a[i].y, a[j].y - a[i].y) % P + P) % P;
        ans = (ans - f[i] * getC(a[i].x + a[i].y - 2, a[i].y - 1) % P + P) % P;
    }
    
    printf("%lld\n", ans);
    return 0;
}
View Code

 

posted @ 2018-09-12 19:22  CzxingcHen  阅读(738)  评论(0编辑  收藏  举报