扩展欧几里得算法
扩欧代码(时间复杂度O(logn))
求ax+by=gcd(a,b)的一组整数解
int gcd(int a,int b)
{
	if(b==0)return a;
	return gcd(b,a%b);
}
int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	int x1,y1,d;
	d=exgcd(b,a%b,x1,y1);
	x=y1,y=x1-a/b*y1;
	return d;
}
扩欧的应用
求不定方程ax+by=c的一组整数解
思路
若gcd(a,b)|c(gcd(a,b)是c的因子),则有整数解
先用扩欧求ax+by=gcd(a,b)的解
再乘以c/gcd(a,b),即得原方程的特解
如果a是负数,就a和gcd都取相反数再解
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
    
int exgcd(int a,int b,int &x,int &y)
{
  if(b == 0) {x=1, y=0; return a;}
  int x1, y1, d;
  d = exgcd(b, a%b, x1, y1);
  x = y1, y = x1-a/b*y1;
  return d;
}
int main()
{
  int a, b, c, x, y;
  cin >> a >> b >> c;
  int d = exgcd(a,b,x,y);
  if(c%d == 0)printf("%d %d",c/d*x,c/d*y);
  else puts("none");
  return 0;
}
给定整数a,b,m,求解同余方程ax≡b(mod m),如果x存在整数解,输出任意一个,不存在输出none
思路
把同余方程转化为不定方程,
由ax≡b(mod m)
得ax=m(-y)+b
ax+my=b
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
    
int exgcd(int a,int b,int &x,int &y){
  if(b == 0){x = 1, y = 0; return a;}
  int x1, y1, d;
  d = exgcd(b, a%b, x1, y1);
  x = y1, y = x1-a/b*y1;
  return d;
}
int main(){
  int a, b, m, x, y;
  scanf("%d%d%d", &a, &b, &m);
  int d = exgcd(a, m, x, y);
  if(b%d == 0) 
    printf("%d", 1ll*x*b/d%m);
  else puts("none");
  return 0;
}
a与m互质时,对于方程ax≡1(mod m),求a的乘法逆元x(0<x<m)
逆元仅在所求数与模数互质时存在
扩展欧几里得算法应该是最优的求逆元算法之一,它和费马小定理具有同样的时间复杂度O(log(n)),但是费马小定理需要模数为质数,扩展欧几里得算法则不需要。
思路
转化为同余方程,等价于ax+my=1
(x%m+m)%m即为答案,这是为了得到最小正整数
ax+my=b 最小整数解的公式:((x0*(b/gcd(a,m)))%(m/gcd(a,m))+(m/gcd(a,m)))%(m/gcd(a,m))
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
    
int exgcd(int a,int b,int &x,int &y){
  if(b == 0){
    x = 1, y = 0; return a;
  }
  int x1, y1, d;
  d = exgcd(b, a%b, x1, y1);
  x = y1, y = x1-a/b*y1;
  return d;
}
int main(){
  int a, m, x, y;
  scanf("%d%d", &a, &m);
  exgcd(a, m, x, y);
  printf("%d", (x%m+m)%m);
  return 0;
}
洛谷例题传送门,题解更详细 洛谷P3811
还有一道类似的题也可以看看 洛谷P5431
由于n以内所有正整数都在模p意义下有逆元,所以p和所有数互质,p为质数
设invi为i的逆元,则有递推式衍生出的代码:
inv[1] = 1;
for(int i = 2; i < p; ++ i)
    inv[i] = (p - p / i) * inv[p % i] % p;
我们还可以O(n)求出阶乘逆元,因为有如下递推关系
inv[i+1]=1/(i+1)!
inv[i+1]*(i+1)=1/i!=inv[i]
所以我们可以先求出n!的逆元,逆推求出(n-1)! 、(n-2)!等的逆元
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号