codeforces(559C)-C. Gerald and Giant Chess(组合数学)
codeforces(559C)--C. Gerald and Giant Chess(组合数学)
题目大意:给出一个棋盘为h*w,现在要从(1,1)到(h,w),其中有n个黑点不能走,问有多少种可能从左上到右下(1,1和h,w永远是可以走的)
计算左上到右下的方法如果不考虑黑点的话,sum=C(h+w)(h)
因为存在黑点i(x,y),所以用所以计算从左上到黑点的方法有sum[i] = C(x+y)(x),其中如果在黑点的左上还有黑点j(u,v),那么应该减去sum[j]*C(x-u+y-v)(y-u),去掉所有在左上的黑点的影响就可以得到由左上到第i点的真正的方法数
从左上的第一个黑点,一直计算到右下(h,w)
注意:
1、C(h+w)(h)的数据很大,C(h+w)(h) = (h+w)!/( h!*w! ),用数组fac记录下每个数的阶乘
2、计算组合数的时候有除法,可以用逆元来做a/b%mod = a*(b^(mod-2))%mod,计算i的阶乘的逆元inv[i],第在对阶乘求逆元的方法还有inv[ fac[i] ] = inv[ fac[i+1] ]*(i+1)%mod ,这个方法仅对连续的阶乘有效。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std ; #define LL __int64 const LL MOD = 1e9+7 ; struct node{ LL x , y ; }p[3100]; LL h , w , n ; LL fac[310000] , inv[310000] ; LL sum[3100] ; int cmp(node a,node b) { return a.x < b.x || (a.x == b.x && a.y < b.y) ; } LL pow(LL x,LL k) { LL ans = 1 ; while( k ) { if( k&1 ) ans = ans*x%MOD ; k = k>>1 ; x = (x*x)%MOD ; } return ans ; } void init() { LL i , j , c ; fac[0] = inv[0] = 1 ; for(i = 1 ; i <= h+w ; i++) fac[i] = (fac[i-1]*i)%MOD ; c = max(h,w) ; inv[c] = pow(fac[c],MOD-2) ; for(i = c-1 ; i > 0 ; i--) { inv[i] = inv[i+1]*(i+1)%MOD ; } } int main() { LL i , j ; LL ans ; while( scanf("%I64d %I64d %I64d", &h, &w, &n) != EOF ) { init() ; for(i = 0 ; i < n ; i++) scanf("%I64d %I64d", &p[i].x, &p[i].y) ; p[n].x = h ; p[n++].y = w ; sort(p,p+n,cmp) ; int x1 , y1 , x2 , y2 ; for(i = 0 ; i < n ; i++) { x1 = p[i].x-1 ; y1 = p[i].y-1 ; sum[i] = fac[x1+y1]*inv[x1]%MOD*inv[y1]%MOD ; for(j = 0 ; j < i ; j++) { if( p[j].x <= p[i].x && p[j].y <= p[i].y ) { x2 = x1 - p[j].x+1 ; y2 = y1 - p[j].y+1 ; sum[i] = (sum[i]-fac[x2+y2]*inv[x2]%MOD*inv[y2]%MOD*sum[j]%MOD)%MOD ; if( sum[i] <= 0 ) sum[i] = (sum[i]+MOD)%MOD; } } } printf("%I64d\n", sum[n-1]) ; } return 0 ; }
版权声明:本文为博主原创文章,未经博主允许不得转载。