n<=100000 * m<=100000的网格求不经过给定的h<=2000个点,从左上角到右下角只向右或向下走的方案数。
先来看不经过一个点:
要绕过这个点到右下角,可以先到该点右上角,再下来,或者到该点左下角,再往右。更一般的,是到该点右下角的总方案,减去经过该点的方案数。
如果这个点坐标(n,m),那么到右下角,方案数就是S(n+1,m+1)-S(n,m),其中S(i,j)=C(i+j,j),是到点(i,j)的所有方案数,自行推导。
那如果有多个点呢?我们需要不重复、不遗漏地统计“经过某个点的所有方案”。由于只向右向下,所以统计经过点(i,j)的方案时,必须保证不经过(x,y),x<=i,y<=j的所有点。
那么就按x排序再按y排序所有点,令f(i)表示到点i,不经过任何i左上方的点的方案数,则f(i)=S(Xi-1,Yi-1)-f(j)*S(Xi-Xj,Yi-Yj),其中Xj<=Xi,Yj<=Yi。先走合法方案到某个点,后面乱走到点i,这样统计方案一定不重复,因为f(j)表示的方案一定不含经过点k,Xk<=Xj,Yk<=Yj的方案,所以f(j)转移过来的方案都没经过他左上方的所有点,这样即使k的方案中可能有经过j,也不会重复。
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<algorithm> 5 //#include<assert.h> 6 //#include<iostream> 7 using namespace std; 8 9 int n,m,h; 10 #define maxh 2011 11 int f[maxh]; 12 struct Point 13 { 14 int x,y; 15 bool operator < (const Point &b) const 16 {return x<b.x || (x==b.x && y<b.y);} 17 }a[maxh]; 18 #define maxn 200011 19 int fac[maxn],inv[maxn]; 20 const int mod=1e9+7; 21 int powmod(int a,int b) 22 { 23 int ans=1; 24 for (;b;a=1ll*a*a%mod,b>>=1) if (b&1) ans=1ll*ans*a%mod; 25 return ans; 26 } 27 void pre(int n) 28 { 29 fac[0]=1;for (int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod; 30 inv[n]=powmod(fac[n],mod-2);for (int i=n-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod; 31 } 32 int C(int n,int m) {return 1ll*fac[n]*inv[n-m]%mod*inv[m]%mod;} 33 int S(int n,int m) {return C(n+m,n);} 34 int main() 35 { 36 scanf("%d%d%d",&n,&m,&h); 37 for (int i=1;i<=h;i++) scanf("%d%d",&a[i].x,&a[i].y); 38 sort(a+1,a+1+h); 39 a[++h].x=n;a[h].y=m; 40 pre(n+m+2); 41 f[1]=S(a[1].x-1,a[1].y-1); 42 for (int i=2;i<=h;i++) 43 { 44 f[i]=S(a[i].x-1,a[i].y-1); 45 for (int j=1;j<i;j++) 46 if (a[i].y>=a[j].y) f[i]-=1ll*f[j]*S(a[i].x-a[j].x,a[i].y-a[j].y)%mod, 47 f[i]+=f[i]<0?mod:0; 48 } 49 printf("%d ",f[h]); 50 return 0; 51 }