一道花了很长时间搞懂的题
We have a large square grid with H rows and W columns. Iroha is now standing in the top-left cell. She will repeat going right or down to the adjacent cell, until she reaches the bottom-right cell.
However, she cannot enter the cells in the intersection of the bottom A rows and the leftmost B columns. (That is, there are A×B forbidden cells.) There is no restriction on entering the other cells.
Find the number of ways she can travel to the bottom-right cell.
Since this number can be extremely large, print the number modulo 10^9+7.
题目链接
给你一个矩形,高H,宽W,然后在它的左下角有一个高为a,宽为b的子矩形,现在有一个人想走最短距离从左上角(1,1)走到右下角(H,W)且不经过左下角的矩形,求总方案数,由于答案可能很大,答案要模(10^9+7)。
1≦H,W≦100,000 1 ≦ H , W ≦ 100 , 000
1≦A<H 1 ≦ A < H
1≦B<W 1 ≦ B < W
考试时看到这道题,根本就没有想到正解!
而我写出一个dp递推式:
好了,正解降临!
如果没有左下角的矩形,我们是不是稍稍举个例子就能发现一点规律?
我们举一个H为4,W为5,到每个点方案数的例子:
看看看!看到没有那里有一个(4,6,4,1)是不是很熟悉??好像是(1,4,6,4,1)吧?
好像我们在哪里见到过,杨辉三角!
是不是就是杨辉三角放倒了?
然后我们知道杨辉三角与组合(C)密切相关,于是我们就可以开始考虑组合方面的问题了.
如上图,我们可以将s->t的路径分为s->i->t,i在H-a那一行,那么我们就显然可以得出i在横向位置方面的取值范围:
②i->t
跟①一样我们来计算一下i->t的步数:
i->t纵向上步数:a
i->t横向上步数:W-i
i->t总步数:a+W-i
But
我们可以发现,如果我们这样干的话会重复计数,比如当前i再往右走一步,就和i+1的方案数重复了于是,到了i只能往下走.于是我们可一发现,这里(H-a+1,i)->(H,W)的方案数就是(H-a,i)->(H,W)的方案数,所以纵向上是a-1步而不是a步,于是这里应该为:
i->t纵向上步数:a-1
i->t横向上步数:W-i
i->t总步数:W+a-i-1
所以应该是:C(W+a-i-1,a-1)或C(W+a-i-1,W-i)(计算方法同上)
总方案就是
这个嘛,哼哼哼,学了之后发现MOD是’套路’质数后就简单许多,这里使用来算C(排列)的,乘法逆元部分详见博主另一篇博客,自我感觉比较详细~~..,好吧,我承认,我把最重要的另一部分放在了另一篇博客增加阅读量……那篇博客中这道题会出现成为两道乘法逆元实现例子之一…(**超级打广告**)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MOD 1000000007
#define MAXN 100000
#define LL long long
#define INF 0x3f3f3f3f
LL read(){
LL f=1,x=0;
char c=getchar();
while(c<'0'||'9'if(c=='-')f=-1;c=getchar();}
while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
return f*x;
}
LL n,h,w,a,b,s[2*MAXN+5]={1},inv[2*MAXN+5];
LL QuPow(LL x,LL y){//快速幂
LL ret=1;
while(y){
if(y&1) ret=ret*x%MOD;
x=x*x%MOD;
y>>=1;
}
return ret;
}
void Prepare(){//乘法逆元处理
for(int i=1;i<=2*MAXN;i++) s[i]=s[i-1]*i%MOD;
inv[0]=1;
inv[2*MAXN]=QuPow(s[2*MAXN],MOD-2);
for(int i=2*MAXN-1;i>=1;i--)
inv[i]=inv[i+1]*(i+1)%MOD;
return ;
}
LL C(LL m,LL n){//计算排列
return s[m]*inv[n]%MOD*inv[m-n]%MOD;
}
int main(){
LL ans=0;
Prepare();
h=read(),w=read(),a=read(),b=read();
for(int i=b+1;i<=w;i++)//推导计数方法
ans=(ans+C(h-a+i-2,i-1)*C(a+w-i-1,a-1)%MOD)%MOD;
printf("%lld\n",ans);
return 0;
}