题目训练网址(密码hpuacm) https://vjudge.net/contest/240634#overview

关于同余定理及其性质的介绍参考这篇博文 https://blog.csdn.net/codeharvest/article/details/70314593

关于逆元以及求解逆元的扩展欧几里得算法的介绍参考这篇博文  https://blog.csdn.net/stray_lambs/article/details/52133141

同余定理两个整数同时除以一个整数所得余数相同,则二整数同余。记作a ≡ b(mod m)。

总的来说同于定理有两点常用

1. 和的取余等于取余的和的取余      (a + b) % m = (a % m + b % m) % m

2. 乘积的取余等于取余的乘积取余  (a * b) % m = ((a % m) * (b % m)) % m

证明: 1

设 a = k1 * m + r1,b = k2 * m + r2
则 (a + b) % m = ((k1 * m + r1) + (k2 * m + r2)) % m
               = ((k1 + k2) * m + (r1 + r2)) % m
               = (r1 + r2) % m
               = (a % m + b % m) % m
所以 (a + b) % m = (a % m + b % m) % m

证明: 2

设 a = k1 * m + r1,b = k2 * m + r2
则 (a * b) % m = ((k1 * m + r1) * (k2 * m + r2)) % m
               = (k1 * k2 * m^2 + (k1 * r2 + k2 * r1) * m + r1 * r2) % m
               = (r1 * r2) % m
               = ((a % m) * (b % m)) % m
所以 (a * b) % m = ((a % m) * (b % m)) % m

逆元与扩展欧几里得算法

逆元:如果 a*x ≡ 1(mod m),这里我们称x是a关于m的乘法逆元。这怎么求?可以等价于这样的表达式: a*x + m*y = 1,这样用下文的exgcd算法求解出来的x就是a关于m的乘法逆元

a*x≡1(mod m) 等价于a*x+m*y=1 可以用扩展欧几里得求
得一组解,(x+m)mod m即为a的逆元

设c是b的逆元,则有b*c≡1(mod m)
推论:(a/b)mod m 
     = (a/b)*1mod m
     = (a/b)*b*c mod m
     = a*c(mod m)

即a/b的模等于a*(b的逆元)的模

算法代码实现

int exgcd( int a, int b, int &x, int &y )   // 求出来的x是a关于b的逆元
{
    if( b == 0 )
    {
        x = 1;
        y = 0;
        return a;   // 最大公约数
    }
    int r = exgcd( b, a%b, x, y );  // 这里的x,y就是x2,y2, 这是第二个方程
    int t = x;
    x = y;
    y = t - (a/b)*y;
    return r;            // 返回值是a和b的最大公约数
}

a*x+b*y=m

a*x1+b*y1=gcd(a,b), b*x2 +(a%b)* y2=gcd(b,a%b)

解 x,y的方法的理解:

  1显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;

  也就是1*a+0*b = a

  2 ax1+by1=gcd(a,b);

          在下一个exgcdbx2+(a mod b)y2=gcd(b,a mod b);

          根据欧几里德原理有 gcd(a,b)=gcd(b,a mod b);

          则:ax1+by1=bx2+(a mod b)y2;

          即:ax1+by1=bx2+(a-(a/b)*b)y2=ay2+bx2-(a/b)*by2;

          根据恒等定理得:x1=y2; y1=x2-(a/b)*y2;

        这样我们就得到解 x1,y1 的方法:x1,y1 的值基于 x2,y2

题目讲解:A青蛙的约会

这道题我们分析在一个数轴上有两只青蛙,这个数轴是首尾交接的,所以可以一直围着它走,显然我们可以列出一个方程来。设它们走了t步,和他们追击了k圈,也就是围着又走了k圈。 
所以 x + m * t = y + n * t + k * L。 
我们转换一下方程: 
(n - m) * t + L * k = x - y
所以它是形如ax+by=c这种形式,我们直接用欧几里得求出一组解输出最小正解就好了。 

#include <cstdio>
using namespace std;
typedef long long LL;

LL exgcd( LL a, LL b, LL &x, LL &y )
{
    if( b == 0 )
    {
        x = 1;
        y = 0;
        return a;
    }
    LL r = exgcd( b, a%b, x, y );
    LL t = x;
    x = y;
    y = t - (a/b) * y;
    return r;
}
int main()
{
    LL a, b, m, n, l;
    LL x, y;
    scanf("%lld%lld%lld%lld%lld", &a, &b, &m, &n, &l );
    // 所求方程 (n - m) * t + L * k = a - b        t相当于x,k相当于y,两个自变量
    LL c = a - b;
    LL d = exgcd( n-m, l, x, y );   // d为n-m与l的最大公约数
    if( c%d !=0 )   // 无解的条件
        printf("Impossible\n");
    else
    {
        x = x * c /d;     // 之前求出的x是所求方程右边是gcd时的x,乘上c除以的正好得到了右边是a-b时的x
        y = y * c /d;
        x = (x%l + l) % l;  // 求的最小的正数x
        printf("%lld\n", x );
    }


    return 0;
}