关于逆元,从网上看到一句话:“除以一个数再取模等同于乘以这个数的逆元再取模”很好。这可以帮助我们去理解什么是逆元。
要注意:对于逆元,我们都是说一个数x在模p的条件下的逆元是多少。而不是直接去说一个数的逆元是多少。
其次,我们不难得知一个数的逆元有无穷多个,但是我们只需要求得一个数的最小正整数逆元就行了。
另外,一个数在模p的条件下不一定有逆元,x关于p的逆元存在当且仅当x和p互质。
求逆元有两种方法:
//by Judge
inline ll quick_pow(ll x,rint p){
ll res=1;
while(p){
if(p&1) res=(res*x)%mod;
x=(x*x)%mod, p>>=1;
}
return res;
}
inline inv(ll a){
inv_a=quick_pow(a,mod-2);
return inv_a;
}
下面我们不着急讲第二种算法,先来讲讲这种快速模算法的应用:
求 C(n,r) % p 的值 , 其中 p 是大质数(如1e9+7) ,那么最后的答案是: 原式 = n! * (r! * (n-r)!)^(p-2) %p
推导过程:
令temp=r!*(n-r)!
那么:C(n,r)%p=(n!/temp)%p
根据我们上面说过的除以一个数取模等于乘上这个数的逆元再取模。
所以:(n!/temp)%p=(n!*temp逆元)%p
而temp逆元根据我们的蒙哥马利快速幂模可知为temp^(p-2)
故结果为
n! * (r! * (n-r)!)^(p-2) %p
代码实现:
//by Judge
#define ll long long
const ll mod=; //1e9+7或者其他题目给定的大质数
inline ll quick_pow(ll x,rint p){
ll res=1;
while(p){
if(p&1) res=(res*x)%mod;
x=(x*x)%mod, p>>=1;
}
return res;
}
inline C_mod(rint n,rint m){
ll a=1,b=1;
for(rint i=2;i<=n<<1;++i)
a=(a*i)%mod;
for(rint i=2;i<=m;++i)
b=(b*i)%mod;
for(rint i=2;i<=n-m;++i)
b=(b*i)%mod;
return (a*quick_pow(b,mod-2))%mod;
}
代码:
//by Judge
#define ll long long
const int mod=; //同上
void ex_gcd(ll a,ll b,ll &x,ll &y){
if(!b){ x=1,y=0; return ; }
ex_gcd(b,a%b,x,y);
ll t=x; x=y,y=t-(a/b)*y;
}
//当然你也可以这么写,更能体现公式:
// ll X=x,Y=y;
// x=Y,y=X-(a/b)*Y;
inline ll inv(ll a){
ll inv_a,y;
ex_gcd(a,mod,inv_a,y);
return inv_a;
}
例题poj1006:
中国剩余定理:
#include<stdio.h>
#define MAX 21252
int main()
{
int p, e, i, d, n, count = 0;
while( scanf("%d%d%d%d", &p, &e, &i, &d) != EOF )
{
count++;
if(p == -1 && e == -1 && i == -1 && d == -1)
{
break;
}
n = ( 5544 * p + 14421 * e + 1288 * i - d ) % MAX;
if( n <= 0 ) // 范围限制
{
n += 21252;
}
printf("Case %d: the next triple peak occurs in %d days.\n", count, n );
}
return 0;
}
求5544,14421,1288时用到了求逆元。