problem
luogu
给一个 H×WH\times WH×W 的网格,每一步只能向右或向下走,给出一些坐标,这些坐标对应的位置不能经过,求从左上角 (1,1)(1,1)(1,1) 走到右下角 (H,W)(H,W)(H,W) 的方案数,答案对 109+710^9+7109+7 取模。
1≤H,W≤1e5,1≤n≤30001\le H,W\le 1e5,1\le n\le 30001≤H,W≤1e5,1≤n≤3000。
solution
设 f(i,j):f(i,j):f(i,j): 在位置 (i,j)(i,j)(i,j) 的方案数显然已经不行了。
关注到障碍物的数量非常少,考虑从障碍物的角度入手。
这种在网格图上只有右边和下边的走法抽象成数学模型就是组合数。
从 (x1,y1)(x_1,y_1)(x1,y1) 到 (x2,y2)(x_2,y_2)(x2,y2) 的方案数即为 (x2−x1+y2−y1x2−x1)\binom{x_2-x_1+y_2-y_1}{x_2-x_1}(x2−x1x2−x1+y2−y1)。
这种方案数包含所有经过不同的若干个障碍物的情况。
我们考虑容斥,至少经过 000 个障碍物 −-− 至少经过 111 个障碍物 +++ 至少经过两个障碍物 …\dots…。
这是普通的容斥,是总集合靠着子集加加减减来求得。
S=A1+(A2−A1⋂A2)+(A3−(A1⋃A2)⋂A3)+...+(An−(A1⋃⋯⋃An−1)⋂An)S=A_1+(A_2-A_1\bigcap A_2)+(A_3-(A_1\bigcup A_2)\bigcap A_3)+...+(A_n-(A_1\bigcup\dots\bigcup A_{n-1})\bigcap A_n)S=A1+(A2−A1⋂A2)+(A3−(A1⋃A2)⋂A3)+...+(An−(A1⋃⋯⋃An−1)⋂An)
每次从剩余元素中,把属于 AiA_iAi 的取走,直到取完总集合。
设 f(i):f(i):f(i): 只经过第 iii 个障碍物的方案数。
将障碍物按 xxx 坐标升序排列,相同则按 yyy 升序。
把最后终点也当作障碍物即可。
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 200005
#define int long long
#define mod 1000000007
int n, r, c;
int f[maxn], fac[maxn], inv[maxn];
struct node { int x, y; }p[maxn];
int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans;
}
int C( int n, int m ) {return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
void init( int n = 2e5 ) {fac[0] = inv[0] = 1;for( int i = 1;i <= n;i ++ ) fac[i] = fac[i - 1] * i % mod;inv[n] = qkpow( fac[n], mod - 2 );for( int i = n - 1;i;i -- ) inv[i] = inv[i + 1] * (i + 1) % mod;
}
signed main() {scanf( "%lld %lld %lld", &r, &c, &n ); init();for( int i = 1;i <= n;i ++ ) scanf( "%d %d", &p[i].x, &p[i].y );p[++ n] = (node){ r, c };sort( p + 1, p + n + 1, []( node a, node b ){ return a.x == b.x ? a.y < b.y : a.x < b.x; } );for( int i = 1;i <= n;i ++ ) f[i] = C( p[i].x + p[i].y - 2, p[i].x - 1 );for( int i = 1;i <= n;i ++ )for( int j = i + 1;j <= n;j ++ )if( p[i].y <= p[j].y )( f[j] -= f[i] * C(p[j].x - p[i].x + p[j].y - p[i].y, p[j].x - p[i].x) ) %= mod;printf( "%lld\n", (f[n] + mod) % mod );return 0;
}