链接:51Nod - 1119
题意:
第1行,2个数M,N,中间用空格隔开。(2 <= m,n <= 1000000)
输出走法的数量 Mod 10^9 + 7。
2 3
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;
}