乘法逆元
定义:
如果有ab≡1(modp)ab≡1(modp),则称b是mod p意义下a的乘法逆元。记b=inv(a)b=inv(a)或b=a−1b=a−1
求法:
1.扩展欧几里得
ax≡1(modp)ax≡1(modp)可以等价的转化为ax−py=1ax−py=1
然后套用exgcd解方程,并检查gcd(a,p)gcd(a,p)是否等于1
如果gcd(a,p)=1gcd(a,p)=1,把xx调整到11~p−1p−1即可
#include<cstdio>
typedef long long LL;
void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){
if (!b) {d = a, x = 1, y = 0;}
else{
ex_gcd(b, a % b, y, x, d);
y -= x * (a / b);
}
}
LL inv(LL t, LL p){//如果不存在,返回-1
LL d, x, y;
ex_gcd(t, p, x, y, d);
return d == 1 ? (x % p + p) % p : -1;
}
int main(){
LL a, p;
while(~scanf("%lld%lld", &a, &p)){
printf("%lld\n", inv(a, p));
}
}
2.费马小定理
由ap−1≡1(mod p)ap−1≡1(mod p)得a×ap−2≡1(mod p)a×ap−2≡1(mod p)
所以当模数是一个质数的时候,可以用费马小定理求解,即inv(i)=ip−2(mod p)inv(i)=ip−2(mod p),复杂度O(log n)。
LL pow_mod(LL a, LL b, LL p){//a的b次方求余p
LL ret = 1;
while(b){
if(b & 1) ret = (ret * a) % p;
a = (a * a) % p;
b >>= 1;
}
return ret;
}
LL Fermat(LL a, LL p){//费马求a关于b的逆元
return pow_mod(a, p-2, p);
}
3.欧拉定理
由aφ(p)≡1(mod p)aφ(p)≡1(mod p)得aφ(p)−1aφ(p)−1是a的逆元
适用于模数不是素数
4.线性递推
当p是素数的时候有
inv(a) = (p - p / a) * inv(p % a) % p
证明:
设x = p % a,y = p / a
于是有 x + y * a = p
(x + y * a) % p = 0
移项得 x % p = (-y) * a % p
x * inv(a) % p = (-y) % p
inv(a) = (p - y) * inv(x) % p
于是 inv(a) = (p - p / a) * inv(p % a) % p
然后一直递归到1为止,因为1的逆元就是1
#include<cstdio>
typedef long long LL;
LL inv(LL t, LL p) {//求t关于p的逆元,注意:t要小于p,最好传参前先把t%p一下
return t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p;
}
int main(){
LL a, p;
while(~scanf("%lld%lld", &a, &p)){
printf("%lld\n", inv(a%p, p));
}
}
中国剩余定理
问题:
一堆物品
3个3个分剩2个
5个5个分剩3个
7个7个分剩2个
问这个物品有多少个
首先假如我们求出这样三个数k1,k2,k3k1,k2,k3,满足k1与3互质且是5和7的倍数,k2与5互质且是3,7的倍数,k3与7互质且是3和5的倍数,那么容易意会得到,k1∗2+k2∗3+k3∗2k1∗2+k2∗3+k3∗2一定会是一个满足题目条件的数。而题目的通解可表示为这个数每次都加上3,5,7的最小公倍数
首先我们求出3,5,7的lcm=105
然后我们令:
x1=105/3=35
x2=105/5=21
x3=105/7=15
然后我们求解以下方程:
a∗x1%3=1
b∗x2%5=1
c∗x3%7=1
用扩展欧几里得求解
a=2,b=1,c=1。
答案就是:
ans=(a∗x1∗2+b∗x2∗3+c∗x3∗2)%lcm=23ans=(a∗x1∗2+b∗x2∗3+c∗x3∗2)%lcm=23
这是最小的一个解,在此之上每加105都可以为一个解。
//n个方程:x=a[i](mod m[i]) (0<=i<n)
LL china(int n, LL *a, LL *m){
LL M = 1, ret = 0;
for(int i = 0; i < n; i ++) M *= m[i];
for(int i = 0; i < n; i ++){
LL w = M / m[i];
ret = (ret + w * inv(w, m[i]) * a[i]) % M;
}
return (ret + M) % M;
}
可是如果要求的几个数a,b,c不互质怎么办?
假设我们这里有两个方程:
x=a1∗x1+b1
x=a2∗x2+b2
a1,a2是模数,b1,b2是余数。
那么我们可以合并这两个方程:
a1∗x1+b1=a2∗x2+b2,由于x1和x2可以取负无穷到正无穷,所以符号不能约束它们
我们随变形得到:a1∗x1+a2∗x2=b2−b1
扩展欧几里德
好的,现在我们求出了一个最小正整数解x1x1,那么令k=(a1∗x1+b1)。
现在我们搞个方程出来:
x≡k(modlcm(a1,a2))
那么一路合并下去就可以得到最终的解答了。
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> PLL;
PLL linear(LL A[], LL B[], LL M[], int n) {//求解A[i]x = B[i] (mod M[i]),总共n个线性方程组
LL x = 0, m = 1;
for(int i = 0; i < n; i ++) {
LL a = A[i] * m, b = B[i] - A[i]*x, d = gcd(M[i], a);
if(b % d != 0) return PLL(0, -1);//答案不存在,返回-1
LL t = b/d * inv(a/d, M[i]/d)%(M[i]/d);
x = x + m*t;
m *= M[i]/d;
}
x = (x % m + m ) % m;
return PLL(x, m);//返回的x就是答案,m是最后的lcm值
}