暑假集训.3(逆元 中国剩余定理)

1.逆元

   逆元怎么求  (a和p互质,a才有关于b的逆元) 

方法一:

   费马小定理

             a^(p-1)=1(mod p)

   两边同除以a

            a^(p-2)=inv(a)(mod p)

            inv(a)=a^(p-2)(mod p)

可以用快速幂求解   复杂度 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);
}

方法二:

扩展欧几里德算法

        a*x+b*y=1

       如果ab互质,则有解。并且这个解的X是a关于b的逆元,解的Y是b关于x的逆元

       a*x % b + b*y % b = 1 % b

       a*x % b = 1 % b

       a*x = 1 (mod b)

代码:   

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include <cmath>
using namespace std;
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;
        return;
    }
    ex_gcd(b,a%b,x,y,d);
    int t=x;x=y;y=t-(a/b)*y;
    return;
}
LL inv(LL t, LL p)
{
     LL d, x, y;
     ex_gcd(t, p, x, y, d);
     if(d==1)
        return (x % p + p) % p;
     else
        return -1;
}
int main()
{
    LL n,m;
    while(~scanf("%lld%lld", &n, &m))
    {
         printf("%lld\n", inv(n, m));
     }

    return 0;
}

 中国剩余定理

能求解什么问题呢?

问题:

一堆物品

3个3个分剩2个

5个5个分剩3个

7个7个分剩2个

问这个物品有多少个?

解这题,我们需要构造一个答案

我们需要构造这个答案

5*7*inv(5*7,  3) % 3  =  1

3*7*inv(3*7,  5) % 5  =  1

3*5*inv(3*5,  7) % 7  =  1

这3个式子对不对,别告诉我逆元你忘了(*´∇`*),忘了的人请翻阅前几章复习

然后两边同乘你需要的数

2 * 5*7*inv(5*7,  3) % 3  =  2

3 * 3*7*inv(3*7,  5) % 5  =  3

2 * 3*5*inv(3*5,  7) % 7  =  2

令 

a = 2 * 5*7*inv(5*7,  3) 

b = 3 * 3*7*inv(3*7,  5) 

c = 2 * 3*5*inv(3*5,  7) 

那么

a % 3 = 2

b % 5 = 3

c % 7 = 2

其实答案就是a+b+c

因为

a%5 = a%7 = 0 因为a是5的倍数,也是7的倍数

b%3 = b%7 = 0 因为b是3的倍数,也是7的倍数

c%3 = c%5 = 0 因为c是3的倍数,也是5的倍数

所以

(a + b + c) % 3 = (a % 3) + (b % 3) + (c % 3) = 2 + 0 + 0 = 2

(a + b + c) % 5 = (a % 5) + (b % 5) + (c % 5) = 0 + 3 + 0 = 3

(a + b + c) % 7 = (a % 7) + (b % 7) + (c % 7) = 0 + 0 + 2 = 2

你看你看,答案是不是a+b+c(。・ω・)ノ゙,完全满足题意

但是答案,不只一个,有无穷个,每105个就是一个答案(105 = 3 * 5 * 7)

根据计算,答案等于233,233%105 = 23

如果题目问你最小的那个答案,那就是23了

例题: 51Nod - 1079 

一个正整数K,给出K Mod 一些质数的结果,求符合条件的最小的K。例如,K % 2 = 1, K % 3 = 2, K % 5 = 3。符合条件的最小的K = 23。

Input

第1行:1个数N表示后面输入的质数及模的数量。(2 <= N <= 10) 
第2 - N + 1行,每行2个数P和M,中间用空格分隔,P是质数,M是K % P的结果。(2 <= P <= 100, 0 <= K < P)

Output

输出符合条件的最小的K。数据中所有K均小于10^9。

Sample Input
3
2 1
3 2
5 3
Sample Output
23      

代码如下:

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include <cmath>
using namespace std;
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;
        return;
    }
    ex_gcd(b,a%b,x,y,d);
    int t=x;x=y;y=t-(a/b)*y;
    return;
}
LL inv(LL t, LL p)
{
     LL d, x, y;
     ex_gcd(t, p, x, y, d);
     if(d==1)
        return (x % p + p) % p;
     else
        return -1;
}

LL C(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;
}
int main()
{
    LL n,m,a[10000],b[10000];
    scanf("%lld", &n);
    for(int i=0;i<n;i++)
            scanf("%lld%lld",&a[i],&b[i]);
    printf("%lld\n", C(n,b,a));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42728740/article/details/81204818