题目
题目大意
一个 的矩形,左下角的 的矩形区域不能走,问左上角走到右下角的方案数模 。
分析
大题分析 - 组合数学
如果无视障碍,从 走到 的方案数是 。
用动规的思想:
,从
到每个格子的方案数如下:
发现把它歪过来就是杨辉三角的样子,事实上,这个转移方程就是杨辉三角的转移方程,即
,如果要从杨辉三角上看组合数的话就是这个样子:
,
,
,
,
,
……注意是从
开始的。
从这里面框出一个平行四边形区域就是这道题的模样了:
所以从
走到
的方案数是
(在杨辉三角中数出来的行数和列数都要减一,才是组合数的答案)即
。由于从
走到
的方案数就是从
走到
的方案数,而后者是
,最开头的结论得证。
枚举 ,由于需要烧过左下角的区域,则每种方案必定经过一个格子 ,那么我们就将 到 分成了两步: 到 , 到 (注意第二步的开始点是 ,否则对于样例1就会出问题,这点我想了)。所以:
细节 - 逆元
组合数的计算公式:
。
有除法,又要模
,除法没有分配率,是不是凉了_(:з」∠)_,由于组合数计算一定能整除,所以用逆元转化一下即可,关于逆元:广告传送门。
所以
。妈妈再也不用担心我的除法取模了
代码
将阶乘和逆元初始化出来。
逆元可以用递推的方式求。
这里的 次方和分数线只是一个记号,但是有同样的分式的性质。
所以计算出 ,即可递推得出其他的逆元
#include<cstdio>
#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);
}