E. Gerald and Giant Chess
time limit per test 2 seconds
memory limit per test 256 megabytes
input standard input
output standard output
Giant chess is quite common in Geraldion. We will not delve into the rules of the game, we'll just say that the game takes place on an h × w field, and it is painted in two colors, but not like in chess. Almost all cells of the field are white and only some of them are black. Currently Gerald is finishing a game of giant chess against his friend Pollard. Gerald has almost won, and the only thing he needs to win is to bring the pawn from the upper left corner of the board, where it is now standing, to the lower right corner. Gerald is so confident of victory that he became interested, in how many ways can he win?
The pawn, which Gerald has got left can go in two ways: one cell down or one cell to the right. In addition, it can not go to the black cells, otherwise the Gerald still loses. There are no other pawns or pieces left on the field, so that, according to the rules of giant chess Gerald moves his pawn until the game is over, and Pollard is just watching this process.
Input
The first line of the input contains three integers: h, w, n — the sides of the board and the number of black cells (1 ≤ h, w ≤ 10^5, 1 ≤ n ≤ 2000).
Next n lines contain the description of black cells. The i-th of these lines contains numbers ri, ci (1 ≤ ri ≤ h, 1 ≤ ci ≤ w) — the number of the row and column of the i-th cell.
It is guaranteed that the upper left and lower right cell are white and all cells in the description are distinct.
Output
Print a single line — the remainder of the number of ways to move Gerald's pawn from the upper left to the lower right corner modulo 10^9 + 7.
http://codeforces.com/contest/560/problem/E
简明题意:一个h*w(<=100000)的棋盘,上面有n(<=2000)个格子是黑色的,不可通行,你从左上角(1,1)出发,每次只能向右或下移动,问移动到右下角(h,w)的方案数是多少?
……好经典的高中基本又经典的排列组合题的加强版啊……
……当时问过高中数学老师这个超大棋盘上不是一个,而是多个点不可通行的时候,不要粗暴的递推,怎么算,他说不清楚啊,我各种泪目啊……
思路:定义一个数组f[i],表示到第i个点不经过之前任何黑点的走法
然后不考虑黑点,从左上角到每个点的走法是C(delta(x)+delta(y),delta(x));
问题是,如果我在考虑第i个黑点的时候,这个黑点的左上角矩形区域里还有其他黑点怎么办?
枚举所有在这个黑点 i 左上角区域的其他黑点,假设已经算好这些黑点 j 相应的 f[j] ,那么我们在统计 f[i] 的时候,答案中减去 f[ j ]*( j 到 i 的所有走法,包括经过其他黑点的 )
——这样处理是不会有重复计数的。比如4个点(2,2)(3,3)(4,4)(6,7),计算到(6,7)不经过其他黑点的走法f [ 3 ],f[ 0 ]中走过1、2号点的情况全部考虑了,f[ 1 ]中一定不会走过0号点,走不走过2号点的情况也全部考虑,f[ 2 ]中一定没走过1、2号点,该考虑的不重复、不遗漏的考虑全面了
同理,统计最后结果的时候也要相应减去 f [ i ] * ( i 到右下角的所有走法,包括经过其他黑点的 )
为了这一题实现的方便,将所有点按从上到下,从左到右排序,也就不用担心左上角的点的结果居然没算的尴尬了
当然,预处理出1 ! ~200000 ! 是比较显然的,但是一开始以为求逆元快的飞起,没有预处理相应的逆元,TLE 20到哭瞎,简直醉人……
(mod后负数的结果我最后再去修正了,反正也不影响结果正确性的)
总体来说这个题不难啊……比赛的时候来不及看题,没去想,太后悔了……
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> using namespace std; typedef long long LL; const LL tomod=1000000007; void extgcd(LL a,LL b,LL& d,LL& x,LL& y){ if(!b){ d=a;x=1;y=0; }else{ extgcd(b,a%b,d,y,x); y-=x*(a/b); } } LL mod_inv(LL a,LL n){//a在模n意义下的逆元 LL d,x,y; extgcd(a,n,d,x,y); return d==1?(x+n)%n:-1; } LL frac[200005]; LL inv[200005]; LL comb(int c,int take){ LL ans=frac[c]; ans=(ans*inv[take])%tomod; ans=(ans*inv[c-take])%tomod; return ans; } struct Point{ int x,y; bool operator<(const Point &b)const{ if(x!=b.x)return x<b.x; return y<b.y; } }p[10005]; LL f[10005]; int main(){ frac[0]=inv[0]=frac[1]=inv[1]=1; for(int i=2;i<=200000;i++){ frac[i]=(frac[i-1]*i)%tomod; inv[i]=mod_inv(frac[i],tomod); } int h,w,n; scanf("%d%d%d",&h,&w,&n); for(int i=0;i<n;i++){ scanf("%d%d",&p[i].x,&p[i].y); } sort(p,p+n); LL ans=comb(h+w-2,h-1); for(int i=0;i<n;i++){ f[i]=comb(p[i].x+p[i].y-2,p[i].x-1); for(int j=0;j<i;j++){ if(p[j].x<=p[i].x&&p[j].y<=p[i].y){ f[i]=(f[i]-f[j]*comb(p[i].x-p[j].x+p[i].y-p[j].y,p[i].x-p[j].x))%tomod; } } ans=(ans-f[i]*comb(h-p[i].x+w-p[i].y,h-p[i].x))%tomod; } ans=(ans+tomod)%tomod; printf("%I64d\n",ans); return 0; }