首页 > 技术文章 > 【Gym 100947E】Qwerty78 Trip(组合数取模/费马小定理)

flipped 2016-07-06 10:26 原文

从(1,1)到(n,m),每次向右或向下走一步,,不能经过(x,y),求走的方案数取模。
可以经过(x,y)则相当于m+n步里面选n步必须向下走,方案数为

C((m1)+(n1)n1)


再考虑其中经过(x,y)的方案数,也就是(1,1)到(x,y)的方案乘上(x,y)到(n,m)的方案,为

C((x1)+(y1),x1)×C((nx)+(my),nx)


于是答案就是下式取模

C(m+n2n1)C(x+y2,x1)×C(nx+my,nx)


m和n大到10的五次方,而组合数要用除法,所以要用费马小定理来求逆元。简单的说就是

C(n,m)%M=n!%M×(m!)1%M×[(nm)!]1%M

而逆元用费马小定理和快速幂来算

(m!)1=qpow(m!,M2)

最后减法取模记得要再加M再取一次模。

#include<bits/stdc++.h>
#define N 200005
#define M 1000000007
#define ll long long
using namespace std;
ll n,m,t,x,y,fac[N]= {1};
ll qpow(ll a,ll b)
{
    ll ans=1;
    ll k=a;
    while(b)
    {
        if(b&1)ans=ans*k%M;
        k=k*k%M;
        b>>=1;
    }
    return ans;
}
ll C(ll n,ll m)
{
    if(m>n)return 0;
    return fac[n]*qpow(fac[m],M-2)%M*qpow(fac[n-m],M-2)%M;
}
int main()
{
    for(int i=1; i<N; i++)fac[i]=fac[i-1]*i%M;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d",&n,&m,&x,&y);
        printf("%lld\n",((C(n+m-2,n-1)-C(x+y-2,x-1)*C(n-x+m-y,n-x)%M+M)%M));
    }
    return 0;
}

 

  

 

推荐阅读