1. 程式人生 > >51nod1486 大大走格子

51nod1486 大大走格子

如果 ring 需要 右上角 () style 網格 namespace str

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\n",f[h]); 50 return 0; 51 }
View Code

51nod1486 大大走格子