【组合数学】【逆元】AtCoder - 1974·いろはちゃんとマス目 / Iroha and a Grid

题目

传送门

题目大意

一个 H×W H × W 的矩形,左下角的 A×B A × B 的矩形区域不能走,问左上角走到右下角的方案数模 109+7 10 9 + 7

分析

大题分析 - 组合数学

如果无视障碍,从 (x,y) ( x , y ) 走到 (m,n) ( m , n ) 的方案数是 Cnymx+ny C m − x + n − y n − y


用动规的思想: dp[i][j]=dp[i1][j]+dp[i][j1] d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] ,从 (1,1) ( 1 , 1 ) 到每个格子的方案数如下:
方案
发现把它歪过来就是杨辉三角的样子,事实上,这个转移方程就是杨辉三角的转移方程,即 Cnm=Cnm1+Cn1m 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)走到 (x,y) ( x , y ) 的方案数是 Cy1x+y11 C x + y − 1 − 1 y − 1 (在杨辉三角中数出来的行数和列数都要减一,才是组合数的答案)即 Cy1x+y2 C x + y − 2 y − 1 。由于从 (x,y) ( x , y ) 走到 (m,n) ( m , n ) 的方案数就是从 (1,1) ( 1 , 1 ) 走到 (mx+1,ny+1) ( m − x + 1 , n − y + 1 ) 的方案数,而后者是 C(ny+1)1(mx+1)+(ny+1)2=Cnymx+ny C ( m − x + 1 ) + ( n − y + 1 ) − 2 ( n − y + 1 ) − 1 = C m − x + n − y n − y ,最开头的结论得证。


枚举 B+1iW B + 1 ≤ i ≤ W ,由于需要烧过左下角的区域,则每种方案必定经过一个格子 (HA,i) ( H − A , i ) ,那么我们就将 (1,1) ( 1 , 1 ) (H,W) ( H , W ) 分成了两步: (1,1) ( 1 , 1 ) (HA,i) ( H − A , i ) (HA+1,i) ( H − A + 1 , i ) (H,W) ( H , W ) (注意第二步的开始点是 (HA+1,i) ( H − A + 1 , i ) ,否则对于样例1就会出问题,这点我想了)。所以:

Ans=i=B+1W(Ci1HA1+i1×CWiH(HA+1)+Wi)=i=B+1W(Ci1HA+i1×CWiA+Wi1) A n s = ∑ i = B + 1 W ( C H − A − 1 + i − 1 i − 1 × C H − ( H − A + 1 ) + W − i W − i ) = ∑ i = B + 1 W ( C H − A + i − 1 i − 1 × C A + W − i − 1 W − i )

细节 - 逆元

组合数的计算公式: Cnm=m!n!(mn)! C m n = m ! n ! ( m − n ) !
有除法,又要模 109+7 10 9 + 7 ,除法没有分配率,是不是凉了_(:з」∠)_,由于组合数计算一定能整除,所以用逆元转化一下即可,关于逆元:广告传送门
所以 Cnm%M=m!n!1(mn)!1%M C m n % M = m ! · n ! − 1 · ( m − n ) ! − 1 % M 妈妈再也不用担心我的除法取模了

代码

将阶乘和逆元初始化出来。
逆元可以用递推的方式求。

inv[i!]i!11i!i+1(i+1)!(i+1)!1(i+1)inv[i+1](i+1) i n v [ i ! ] ≡ i ! − 1 ≡ 1 i ! ≡ i + 1 ( i + 1 ) ! ≡ ( i + 1 ) ! − 1 · ( i + 1 ) ≡ i n v [ i + 1 ] · ( i + 1 )

这里的 1 − 1 次方和分数线只是一个记号,但是有同样的分式的性质。
所以计算出 inv[n!] i n v [ n ! ] ,即可递推得出其他的逆元

#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);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值