您的位置:首页 > 其它

51Nod - 1119(组合数+逆元)

2018-01-29 11:22 267 查看
链接:51Nod - 1119

题意:

M * N的方格,一个机器人从左上走到右下,只能向右或向下走。有多少种不同的走法?由于方法数量可能很大,只需要输出Mod 10^9 + 7的结果。

Input
第1行,2个数M,N,中间用空格隔开。(2 <= m,n <= 1000000)


Output
输出走法的数量 Mod 10^9 + 7。


Input示例
2 3


Output示例
3


题解:这题数据比较大直接暴力肯定是不行咯,通过一部分打表我们不难发现这个矩阵就是由两个杨辉三角构成的,那么求f(n, m)就是求组合数c(m+n-2, m-1)%mod,其中n>=m;

我们令m+n-2=n, m-1=m, 即我们要求c(n, m)=n!/((n-m)!*m!)%mod,为了书写方便,我们再令:a=n!/(n-m)!, b=m!;

那么我们现在要求的就是:(a/b)%mod,除法取模并不能直接计算,我们需要将之转化为乘法取摸运算;

接下来我们可以有两种解法:

解法1:

(a/b)%mod=(a*b')%mod,其中b'为b%mod的乘法逆元,求乘法逆元我们直接用exgcd就好了;不过这里还有一个问题需要注意:

a, b两个数本身就已经超过long long了,所以我们不能先直接计算出a, b的值再求逆元;那么我们是否可以在计算a, b的过程中给其取摸呢?

即:((a%mod)/(b%mod))%mod=?((a%mod)*b')%mod,  答案是可以的, 因为:b=1(%mod), 那么有 b%mod=1(%mod),  显然,先给b取摸再求逆是可行的。 所以我们最终要求的就是:((a%mod)*b')%mod;

解法2:

我们先引入费马小定理:对于互质的两个数b, mod, 有:b^(mod-1)=1(%mod)-----1式;

本题要求 x=(a/b)%mod, 即: a/b=x(%mod)-----2式;

联立1,2式,有:a/b*b^(mod-1)=x(%mod), 即:a*b^(mod-2)=x(%mod), 所以:x=a*b^(mod-2) % mod, 我们可以用快速幂求解;

关于上式证明:

1式等价于:b^(mod-1)%mod=1; 即: b^(mod-1)=k*mod+1;

2式等价于:(a/b)%mod=x; 即: a/b=k'*mod+x;

所以有:a/b*b^(mod-1)=k*k'*mod^2+k'*mod+x*k*mod+x;

所以:a/b*b^(mod-1)%mod=x;

所以:a/b*b^(mod-1)=x(%mod), 即原式得证;

#include <bits/stdc++.h>
using namespace std;

const int mod = 1e9 + 7;
long long n, m;

void exgcd(long long a, long long b, long long& x, long long& y)
{
if(!b){
y = 0; x = 1;
return ;
}
exgcd(b, a % b, y, x);
y -= a / b * x;
}

long long pow_mod(long long x, long long n)
{
long long ans = 1;
while(n){
if(n & 1) ans = ans * x % mod;
x = x * x % mod;
n >>= 1;
}
return ans;
}

int main()
{
scanf("%lld%lld", &n, &m);

if(n < m) swap(n, m);
n += m - 2; m--;
long long a = 1, b = 1, x, y;
for(long long i = n, j = 0; j < m; j++, i--) a = a * i % mod;
for(long long i = 2; i <= m; i++) b = b * i % mod;

exgcd(b, mod, x, y); //x为b对mod的逆元
x = (x % mod + mod) % mod;
printf("%lld\n", a * x % mod);

//printf("%lld\n", a * pow_mod(b, mod-2) % mod); //费马小定理

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: