◇题目传送门◆
题目大意
给定 H,W,A,B H , W , A , B , H,W H , W 分别表示矩形的长宽, A,B A , B 表示位于左下角的 A×B A × B 的矩形不能通过,求从左上角到右下角的方案数,答案对 109+7 10 9 + 7 取模。
暴力???仿佛不行。。。
我们举一个例子:
如一个 4×5 4 × 5 的矩形:
稍微操作一下就可以得到:
我们都知道,杨辉三角和组合数有关系。所以说,这东西不是组合计数吗?
不难发现我们需要横着走 i−1 i − 1 步,竖着走 H−A−1 H − A − 1 步,所以说,该方案数为 Ci−1H−A+i−2 C H − A + i − 2 i − 1 或者 CH−A−1H−A+i−2 C H − A + i − 2 H − A − 1 。
同样可知:我们需要横着走 W−i W − i 步,竖着走 A−1 A − 1 步,则可知该段方案数为 CW−iW−i+A−1 C W − i + A − 1 W − i 或者 CA−1W−i+A−1 C W − i + A − 1 A − 1 。
根据乘法原理及加法原理可得,答案为:
我们也可以逆向计算,即用总方案数减去不合法的方案数。
易得总方案数为 CH−1H+W−2 C H + W − 2 H − 1 ,我们就计算一下不合法的方案。
举个例子:
不难发现由 s→i s → i 的方案数为 CH−A−1H−A+i−2 C H − A + i − 2 H − A − 1 ,由 i→t i → t 的方案数为 CA−1A−1+W−i C A − 1 + W − i A − 1 。则可得出,由 s→i→t s → i → t 的方案总数为 CH−A−1H−A+i−2×CA−1A−1+W−i C H − A + i − 2 H − A − 1 × C A − 1 + W − i A − 1
而由于 i i 是可以在 1 1 到 B B 之间滑动的,所以,我们的答案就是:
还有逆元这东西???
你难道没有发现题目中要求对 109+7 10 9 + 7 取模???
模运算没有
而:
还是太年轻了。。。
关于乘法逆元的使用及计算,请点这里。
我们都知道, 109+7 10 9 + 7 是一个“套路”质数
所以, n!m!(n−m)!mod109+7=n!⋅(m!)−1⋅[(n−m)!]−1 n ! m ! ( n − m ) ! mod 10 9 + 7 = n ! ⋅ ( m ! ) − 1 ⋅ [ ( n − m ) ! ] − 1 ,除法就成功地转化为了乘法。
#include
#include
using namespace std;
typedef long long ll;
const int Mod=1e9+7;
const int Maxn=2*100000;
int H,W,A,B;
ll f[Maxn+10],inv[Maxn+10];
int N;
ll PowMod(ll a,int b) {
ll ret=1;
while(b) {
if(b&1)ret=ret*a%Mod;
a=a*a%Mod;
b>>=1;
}
return ret;
}
void Init() {
f[0]=1;
for(int i=1;i<=N;i++)
f[i]=f[i-1]*i%Mod;
inv[0]=1;
inv[N]=PowMod(f[N],Mod-2);
for(int i=N-1;i>0;i--)
inv[i]=inv[i+1]*(i+1)%Mod;
}
ll C(int a,int b) {
if(a<0||b<0)return 1;
return f[a]*inv[b]%Mod*inv[a-b]%Mod;
}
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d %d %d %d",&H,&W,&A,&B);
N=H+W-2;
Init();
ll ans=C(H+W-2,H-1);
for(ll i=1;i<=B;i++) {
ans-=C(H-A+i-2,H-A-1)*C(A-1+W-i,A-1)%Mod;
ans=(ans%Mod+Mod)%Mod;
}
printf("%lld",ans);
return 0;
}