给定一个(n imes m)((n,mle 10^5))的棋盘,棋盘上只有(k)((kle2000))个格子是黑色的,其他格子都是白色的。在棋盘左上角有一个卒,每一步可以向右或者向下移动一格,并且不能移动到黑色格子中。求这个卒从左上角移动到右下角,一共有多少种可能的路线。
我们发现(n,m)很大,不好下手,所以考虑从(k)入手,那么方案数就是总方案数减去走到这个黑点的方案数乘从这个黑点走到终点的方案数。
总方案数非常好求,就是(egin{pmatrix}n+m-2\n-1end{pmatrix})
然后考虑设(f_i)表示从起点走到第(i)个黑点且不经过其他黑点的方案数,那么这个显然也是可以按刚才那个方法容斥求得的,于是有如下转移方程:
[f_i=egin{pmatrix}x_i+y_i-2\x_i-1end{pmatrix}-sum_{j=1}^kf_jegin{pmatrix}x_i-x_j+y_i-y_j\x_i-x_jend{pmatrix}[x_ige x_j,y_ige y_j]
]
(x_ige x_j,y_ige y_j)可以通过排序轻松处理出来。
于是最后答案就是:
[egin{pmatrix}n+m-2\n-1end{pmatrix}-sum_{i=1}^kf_iegin{pmatrix}n-x_i+m-y_i\n-x_iend{pmatrix}
]
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 2e5;
const int p = 1e9 + 7;
using namespace std;
int n,m,k,fac[N + 5],inv[N + 5],ans,f[N + 5];
struct black
{
int x,y;
}a[N + 5];
int C(int n,int m)
{
return 1ll * fac[n] * inv[n - m] % p * inv[m] % p;
}
bool cmp(black a,black b)
{
if (a.x == b.x)
return a.y < b.y;
return a.x < b.x;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
fac[0] = 1;
for (int i = 1;i <= N;i++)
fac[i] = 1ll * fac[i - 1] * i % p;
inv[1] = 1;
for (int i = 2;i <= N;i++)
inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
inv[0] = 1;
for (int i = 1;i <= N;i++)
inv[i] = 1ll * inv[i - 1] * inv[i] % p;
ans = C(n + m - 2,n - 1);
for (int i = 1;i <= k;i++)
scanf("%d%d",&a[i].x,&a[i].y);
sort(a + 1,a + k + 1,cmp);
for (int i = 1;i <= k;i++)
{
f[i] = C(a[i].x + a[i].y - 2,a[i].x - 1);
for (int j = 1;j < i;j++)
if (a[j].x <= a[i].x && a[j].y <= a[i].y)
f[i] -= 1ll * f[j] * C(a[i].x - a[j].x + a[i].y - a[j].y,a[i].x - a[j].x) % p,f[i] %= p;
ans -= 1ll * f[i] * C(n - a[i].x + m - a[i].y,n - a[i].x) % p;
ans %= p;
}
cout<<(ans + p) % p<<endl;
return 0;
}