传送门
一个 H×W H × W 的矩形,左下角的 A×B A × B 的矩形区域不能走,问左上角走到右下角的方案数模 109+7 10 9 + 7 。
如果无视障碍,从 (x,y) ( x , y ) 走到 (m,n) ( m , n ) 的方案数是 Cn−ym−x+n−y C m − x + n − y n − y 。
用动规的思想: dp[i][j]=dp[i−1][j]+dp[i][j−1] d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] ,从 (1,1) ( 1 , 1 ) 到每个格子的方案数如下:
发现把它歪过来就是杨辉三角的样子,事实上,这个转移方程就是杨辉三角的转移方程,即 Cnm=Cnm−1+Cn−1m C m n = C m − 1 n + C m n − 1 ,如果要从杨辉三角上看组合数的话就是这个样子: C00=1 C 0 0 = 1 , C01=1 C 1 0 = 1 , C11=1 C 1 1 = 1 , C02=1 C 2 0 = 1 , C12=2 C 2 1 = 2 , C22=1 C 2 2 = 1 ……注意是从 0 0 开始的。
从这里面框出一个平行四边形区域就是这道题的模样了:
所以从 (1,1) ( 1 , 1 ) 走到 (x,y) ( x , y ) 的方案数是 Cy−1x+y−1−1 C x + y − 1 − 1 y − 1 (在杨辉三角中数出来的行数和列数都要减一,才是组合数的答案)即 Cy−1x+y−2 C x + y − 2 y − 1 。由于从 (x,y) ( x , y ) 走到 (m,n) ( m , n ) 的方案数就是从 (1,1) ( 1 , 1 ) 走到 (m−x+1,n−y+1) ( m − x + 1 , n − y + 1 ) 的方案数,而后者是 C(n−y+1)−1(m−x+1)+(n−y+1)−2=Cn−ym−x+n−y C ( m − x + 1 ) + ( n − y + 1 ) − 2 ( n − y + 1 ) − 1 = C m − x + n − y n − y ,最开头的结论得证。
枚举 B+1≤i≤W B + 1 ≤ i ≤ W ,由于需要烧过左下角的区域,则每种方案必定经过一个格子 (H−A,i) ( H − A , i ) ,那么我们就将 (1,1) ( 1 , 1 ) 到 (H,W) ( H , W ) 分成了两步: (1,1) ( 1 , 1 ) 到 (H−A,i) ( H − A , i ) , (H−A+1,i) ( H − A + 1 , i ) 到 (H,W) ( H , W ) (注意第二步的开始点是 (H−A+1,i) ( H − A + 1 , i ) ,否则对于样例1就会出问题,这点我想了)。所以:
组合数的计算公式: Cnm=m!n!(m−n)! C m n = m ! n ! ( m − n ) ! 。
有除法,又要模 109+7 10 9 + 7 ,除法没有分配率,是不是凉了_(:з」∠)_,由于组合数计算一定能整除,所以用逆元转化一下即可,关于逆元:广告传送门。
所以 Cnm%M=m!⋅n!−1⋅(m−n)!−1%M C m n % M = m ! · n ! − 1 · ( m − n ) ! − 1 % M 。妈妈再也不用担心我的除法取模了
将阶乘和逆元初始化出来。
逆元可以用递推的方式求。
#include
#define LL long long
#define MAXN 100000
#define MOD 1000000007
LL fac[2*MAXN+5];//fac[i]表示i!
LL inv[2*MAXN+5];//inv[i]表示i!的逆元
LL Pow(LL x,LL p){//快速幂求逆元
LL ret=1;
while(p){
if(p&1)
ret=ret*x%MOD;
x=(x*x)%MOD;
p>>=1;
}
return ret;
}
void Prep(int N){
fac[0]=1;
for(int i=1;i<=N;i++)
fac[i]=fac[i-1]*i%MOD;//随时随地记得把模带上
inv[0]=1;//这里要注意一下
inv[N]=Pow(fac[N],MOD-2);
for(int i=N-1;i>=1;i--)
inv[i]=inv[i+1]*(i+1)%MOD;
}
LL C(int m,int n){
return fac[m]*inv[n]%MOD*inv[m-n]%MOD;//计算组合数
}
int main(){
int H,W,A,B;
scanf("%d%d%d%d",&H,&W,&A,&B);
Prep(H+W);
LL Ans=0;
for(int i=B+1;i<=W;i++){
LL tmp1=C(H-A+i-2,i-1);
LL tmp2=C(A+W-i-1,W-i);//不开long long会乘爆
Ans=(Ans+(tmp1*tmp2)%MOD)%MOD;
}
printf("%lld",Ans);
}