51nod1119(除法取模/费马小定理求组合数)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1119

 

题意:中文题诶~

 

思路:这题数据比较大直接暴力肯定是不行咯,通过一部分打表我们不难发现这个矩阵就是由两个杨辉三角构成的,那么求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;

 

代码:

 1 #include <bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 
 5 const ll mod=1e9+7;
 6 
 7 void exgcd(ll a, ll b, ll&x, ll&y){
 8     if(!b){
 9         y=0, x=1;
10         return;
11     }
12     exgcd(b, a%b, y, x);
13     y-=a/b*x;
14 }
15 
16 int main(void){
17     ll n, m, a=1, b=1, x, y;
18     cin >> n >> m;
19     if(n<m){
20         swap(n, m);
21     }
22     n=n+m-2, m-=1;
23     for(ll i=n,j=0; j<m; j++,i--){
24         a=i*a%mod;
25     }
26     for(ll i=2; i<=m; i++){
27         b=b*i%mod;
28     }
29     exgcd(b, mod, x, y);
30     x=(x%mod+mod)%mod;
31     cout << a*x%mod << endl;
32     return 0;
33 }

 

解法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), 即原式得证;

 

代码:

 1 #include <bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 
 5 const ll mod=1e9+7;
 6 
 7 ll get_pow(ll x, ll n){
 8     ll ans=1;
 9     while(n){
10         if(n&1){
11             ans=ans*x%mod;
12         }
13         x=x*x%mod;
14         n>>=1;
15     }
16     return (ans+mod)%mod;
17 }
18 
19 int main(void){
20     ll n, m, a=1, b=1, x, y;
21     cin >> n >> m;
22     if(n<m){
23         swap(n, m);
24     }
25     n=n+m-2, m-=1;
26     for(ll i=n,j=0; j<m; j++,i--){
27         a=i*a%mod;
28     }
29     for(ll i=2; i<=m; i++){
30         b=b*i%mod;
31     }
32     cout << a*get_pow(b, mod-2)%mod << endl;
33     return 0;
34 }

 

posted @ 2017-01-15 15:27  geloutingyu  阅读(838)  评论(0编辑  收藏  举报