codeforces 559 C. Gerald and Giant Chess

 

有一个h行w列的棋盘,里面有一些格子是不能走的,现在要求从左上角走到右下角的方案数(只能向右或者向下走)。

1 ≤ h, w ≤ 105, 1 ≤ n ≤ 2000

如果 n ≤ 20 的话,有一个经典的容斥做法:不经过坏点的方案数=总方案数-经过1个坏点的方案数+经过2个坏点的方案数-...+(-1)n经过n个坏点的方案数

但是发现 n 很大,但在此题中,方案是有序的(类似于DAG),那么可以这么构造

对于第 i 个坏点,设 f[i] 表示从左上角走到 i 的方案数,且 i 是唯一经过的坏点

那么可以用左上角到第 i 个坏点的方案数,减去它左上角的所有坏点的 f[j] × (从 i 到 j 的方案数)

这样的话,对于每一条从左上角到 i 且第一次经过的坏点非 i 的方案全部在第一次经过的坏点处被删除掉了,因此统计不重不漏

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 2010, p = 1e9 + 7;
 4 int h, w, n, f[N], fac[200010], inv[200010];
 5 int pw(int a, int b) {
 6     int r = 1;
 7     for( ; b ; b >>= 1, a = 1ll * a * a % p) if(b & 1) r = 1ll * r * a % p;
 8     return r;
 9 }
10 int C(int n, int m) {
11     return 1ll * fac[n + m] * inv[n] % p * inv[m] % p;
12 }
13 struct P {
14     int x, y;
15 } pt[N];
16 bool operator < (P a, P b) { return a.x == b.x ? a.y < b.y : a.x < b.x; }
17 int main() {
18     scanf("%d%d%d", &h, &w, &n);
19     for(int i = fac[0] = 1 ; i <= h + w ; ++ i) fac[i] = 1ll * fac[i - 1] * i % p, inv[i] = pw(fac[i], p - 2);
20     for(int i = 1 ; i <= n ; ++ i) scanf("%d%d", &pt[i].x, &pt[i].y);
21     pt[++ n] = (P) { h, w };
22     sort(pt + 1, pt + 1 + n);
23     inv[0] = f[0] = 1;
24     for(int i = 1 ; i <= n ; ++ i) {
25         f[i] = C(pt[i].x - 1, pt[i].y - 1);
26         for(int j = 1 ; j < i ; ++ j) {
27             if(pt[j].x <= pt[i].x && pt[j].y <= pt[i].y) {
28                 f[i] = (1ll * f[i] - 1ll * f[j] * C(pt[i].x - pt[j].x, pt[i].y - pt[j].y) % p) % p;
29             }
30         }
31     }
32     printf("%d\n", (1ll * f[n] % p + p) % p);
33 }
codeforces 559 C. Gerald and Giant Chess

 

posted @ 2018-08-07 10:07  KingSann  阅读(119)  评论(0编辑  收藏  举报