参考文章:https://zhuanlan.zhihu.com/p/100567253

欧几里得(辗转反侧相除法)

  1. //辗转相除法,求两个数的最大公因数
  2. int gcd(int a, int b)
  3. {
  4. if (b == 0)
  5. return a;
  6. else
  7. return gcd(b, a%b);
  8. }

要记住的原理就是欧几里得&拓展欧几里得 - 图1
求 gcd(75,48)
image.png

拓展欧几里得

什么是拓展欧几里得呢?这是一种算法,它可以在辗转相除途中求出不定方程 欧几里得&拓展欧几里得 - 图3 的一组解。

裴蜀定理

欧几里得&拓展欧几里得 - 图4 为正整数,则关于 欧几里得&拓展欧几里得 - 图5 的方程 欧几里得&拓展欧几里得 - 图6 有整数解当且仅当欧几里得&拓展欧几里得 - 图7欧几里得&拓展欧几里得 - 图8 的倍数。

只要求 欧几里得&拓展欧几里得 - 图9 的解就好了,对于方程 欧几里得&拓展欧几里得 - 图10 ,只需要令 欧几里得&拓展欧几里得 - 图11 即可。
image.png

通过求 欧几里得&拓展欧几里得 - 图13 的解,可以得出 欧几里得&拓展欧几里得 - 图14 的解
前者等价于 欧几里得&拓展欧几里得 - 图15 ,也就是 欧几里得&拓展欧几里得 - 图16
对比系数发现,我们可以令:
欧几里得&拓展欧几里得 - 图17
和普通的辗转相除法相同,当 欧几里得&拓展欧几里得 - 图18 时递归结束,由上图右下角那个式子可知,此时应令 欧几里得&拓展欧几里得 - 图19

  1. int exgcd(int a, int b, int &x, int &y)
  2. {
  3. if (b == 0)
  4. {
  5. x = 1;
  6. y = 0;
  7. return a;
  8. }
  9. int d = exgcd(b, a % b, x, y), x0 = x, y0 = y;
  10. x = y0;
  11. y = x0 - (a / b) * y0;
  12. //上面三行还能这样写
  13. //int d = exgcd(b, a % b, y, x);//在这里就交换了x、y
  14. //y -= (a / b) * x;
  15. return d;
  16. }

image.png

使用该函数时,xy传入一个变量用于存储最后的结果即可.

  1. int x,y;
  2. exgcd(75, 48, x, y);
  3. //此时x、y就是 75x+48y=gcd(75,48) 的特解了

通解简单记忆一下:(推导可以去看参考文章)
欧几里得&拓展欧几里得 - 图21

来点题目

洛谷| P1082 [NOIP2012 提高组] 同余方程

所谓 欧几里得&拓展欧几里得 - 图22 ,其实就相当于 欧几里得&拓展欧几里得 - 图23 ,移下项就是 欧几里得&拓展欧几里得 - 图24
于是我们可以用拓展欧几里得算法求解
有解的条件是 欧几里得&拓展欧几里得 - 图25 ,即 欧几里得&拓展欧几里得 - 图26欧几里得&拓展欧几里得 - 图27 互质,即 欧几里得&拓展欧几里得 - 图28欧几里得&拓展欧几里得 - 图29 互质,那么通解就是 欧几里得&拓展欧几里得 - 图30 。(上面的小记忆)
那么已知 欧几里得&拓展欧几里得 - 图31 要求最小正整数解,只需求 欧几里得&拓展欧几里得 - 图32 就好了,但因为C++对模的处理,如果 欧几里得&拓展欧几里得 - 图33 是负数,这里得到的是最大负整数解,所以需要再加上一个 欧几里得&拓展欧几里得 - 图34 。出于简化书写,可以统一加上b然后再模b

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. #define FOR(i,n,m) for(int i =n;i<m;++i)
  5. int egcd(int a, int b, int&x, int&y){
  6. if(b == 0){
  7. x = 1, y = 0;
  8. return a;
  9. }
  10. int d = egcd(b, a%b, y, x);
  11. y -= (a / b) * x;
  12. return d;
  13. }
  14. int main()
  15. {
  16. ios::sync_with_stdio(false);
  17. int a, b, x, y;
  18. cin>>a>>b;
  19. egcd(a, b, x, y);
  20. int res = ((x%b)+b)%b;
  21. cout<<res;
  22. return 0;
  23. }