乘法逆元:
常见于一个数对另一个数取模,但前者涉及一个除法,例如 A/B mod C,此时并不能将其视为(A mod C) /( B mod C ) mod C
由欧拉定理
若正整数a,n互质,则对于任意正整数b,
当gcd (a, b)=1时,a 在模 b 意义下的乘法逆元存在
拓展欧几里得定理
定理概述:对于不为0的整数a,b一定存在整数x,y使得
我们只需递归该方程求出x即可得到a在模b意义下的逆元 这个方法单个查找效率似乎也还不错,比后面要介绍的大部分方法都要快(尤其对于 mod p 比较大的时候)。
而且这个做法还有个好处在于,当 a⊥p(互质),但 p 不是质数的时候也可以使用。
void exgcd(ll a, ll b, ll& x, ll& y)
{
if (!b) { x = 1; y = 0; }
else exgcd(b, a % b, y, x), y -= a / b * x;
}
ll Exgcd(ll a, ll b, ll& x, ll& y)//法二
{
if(!b){ x = 1; y = 0; return a; }
ll d = exgcd(b, a%b, x, y);
ll z = x;
x = y; y = z - (a / b) * y;
return d;
}
int main()
{
ll a, p, x, y;
exgcd(a, p, x, y);
x = (x % p + p) % p;// x 是 a 在mod p 下的逆元
/* 法二 */
ll d = Exgcd(a, b, x, y);//求出ax + by = gcd(a, b);的一组特解并返回a,b的最大公约数d
}
快速幂
这个做法要利用 费马小定理
/* 注意: a为正整数 p为素数 且a,p互质 时才能使用 */
ll fpm(ll x, ll power, ll mod)
{
x %= mod;
ll ans = 1;
for (; power; power >>= 1, (x *= x) %= mod)
if(power & 1) (ans *= x) %= mod;
return ans;
}
int main()
{
ll x = fpm(a, p - 2, p); //x为a在mod p意义下的逆元
}
线性算法
用于求一连串数字对于一个mod p的逆元。洛谷P3811
只能用这种方法,别的算法都比这些要求一串要慢。
inv[1] = 1;
for(int i = 1; i < p; ++ i)
inv[i] = (p - p / i) * inv[p % i] % p;
阶乘逆元 O(n)求
因为有如下一个递推关系。