暑期集训 乘法逆元、中国剩余定理

乘法逆元

定义:

如果有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值 
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值