扩展欧几里得(exgcd)与同余详解

exgcd入门以及同余基础

gcd,欧几里得的智慧结晶,信息竞赛的重要算法,数论的...(编不下去了

讲exgcd之前,我们先普及一下同余的性质:

  1. ,那么
  2. ,且p1,p2互质,

有了这三个式子,就不用怕在计算时溢出了。

下面我会用分别表示a与b的最大公约数与最小公倍数。

首先会来学扩欧的同学肯定都会欧几里得算法(即辗转相除法)了吧

而通过观察发现:,先除后乘防溢出。

所以的代码如下:

1 inline int gcd(int a,int b)
2 {return (b==0)?a:gcd(b,a%b);}
3 inline int lcm(int a,int b)
4 {return a/gcd(a,b)*b;


讲exgcd之前先引入一种方程——不定方程

所谓不定方程,是指未知数的个数多于方程个数,且未知数受到某些限制(如要求是有理数、整数或正整数等等)的方程或方程组。

————百度百科

就是形如的方程,其中a,b,c已知。

1.判断是否有解

如果,那么方程无解。

2.转化

方程可转化为

其中

3.求一组特解

接着就用到了exgcd。

我们知道gcd有一个性质

如果,一直循环下去,b将等于0,那么x将等于c/a,y=0。

1 inline void exgcd(int a,int b,int c)
2 {
3     if(!b)
4     {x=c/a;y=0;return;}
5     exgcd(b,a%b,c);
6     x=y;
7     y=(c-a*x)/b;
8     return;
9 }

这就求出了一组特解。

exgcd的模板我也在这摆出来

1 inline void exgcd(int a,int b)
2 {
3     if(!b)
4     {x=1;y=0;return;}
5     exgcd(b,a%b);
6     k=x;x=y;
7     y=k-a/b*y;
8     return;
9 }

这是求时用的,后面讲同余方程会讲。

4.构造通解

我们假设x1,y1是我们求出的一组特解,那么

    


同余类问题

1.单个同余方程

求的是关于x的解

转化一下,就成了,就可以直接套exgcd模板。

2.同余方程组

1.有解的充要条件

2.

下式减上式得

再用exgcd求出y1和y2,

3.关于通解

所有的x mod lcm(p1,p2)有唯一解,这样就可以通过特解,求通解了。

4.至于式子更多的同余方程组,就先联立两个,就可以得出新的方程

再联立下一个。


exgcd用处及题目讲解

1.求同余方程的解

例如这道题P1082

这是一道裸的扩欧模板题,变形之后就是求

套模板即可。

 1 inline void exgcd(int a,int b)
 2 {
 3     if(b==0)
 4     {x=1;y=0;return;}
 5     exgcd(b,a%b);
 6     k=x;x=y;
 7     y=k-a/b*y;
 8     return;
 9 }
10 int main()
11 {
12     int n,m;
13     read(n),read(m);
14     exgcd(n,m);
15     printf("%d",(x+m)%m);
16 }

还有一道模板P1516

仔细观察,推一下后我们发现,这在就是在求的解。

进而可以推出

合并同类项后

把一些东西移到左边来后

把(x-y),(n-m)各看成一个整体后,问题就成了解一个不定方程。

 1 inline int exgcd(long long a,long long b)
 2 {
 3     if(b==0)
 4     {x=1;y=0;return a;}
 5     ans=exgcd(b,a%b);
 6     k=x;x=y;
 7     y=k-a/b*y;
 8     return ans;
 9 }
10 int main()
11 {
12     long long x1,y1,m,n,l;
13     read(x1),read(y1),read(m),read(n),read(l);
14     if(n-m<0)swap(x1,y1);
15     exgcd(std::abs(n-m),l);
16     if((x1-y1)%ans!=0)
17     printf("Impossible");
18     else
19     printf("%lld",((x*((x1-y1)/ans))%(l/ans)+(l/ans))%(l/ans));
20 }

还有一道也是模板P4777,涉及同余方程组求解,上面已详细的讲了,近期我也会发一篇中国剩余定理的博客

 1 inline long long mul(long long a,long long b,long long mod)
 2 {
 3     long long res=0;
 4     while(b>0)
 5     {
 6         if(b&1) res=(res+a)%mod;
 7         a=(a+a)%mod;
 8         b>>=1;
 9     }
10     return res;
11 }
12 long long exgcd(long long a,long long b,long long &x,long long &y)
13 {
14     if(!b)
15     {x=1;y=0;return a;}
16     long long gcd=exgcd(b,a%b,x,y);
17     k1=x;x=y;
18     y=k1-a/b*y;
19     return gcd;
20 }
21 int main()
22 {
23     io::begin();
24     io::read(n);
25     for(register int i=1;i<=n;i++)
26     io::read(b1[i]),io::read(a1[i]);
27     long long x,y,k;
28     long long m=b1[1],ans=a1[1];
29     for(int i=2;i<=n;i++)
30     {
31         long long a=m,b=b1[i],c=(a1[i]-ans%b+b)%b;
32         long long gcd=exgcd(a,b,x,y);
33         long long p=b/gcd;
34         x=mul(x,c/gcd,p);
35         ans+=x*m;
36         m*=p;
37         ans=(ans%m+m)%m;
38     }
39     printf("%lld",(ans%m+m)%m);
40 }

2.扩欧求逆元

这是一种很重要的算法,至于逆元怎么跟扩欧扯上关系,大家可以点这里乘法逆元及两道模板题详解

这里就不多赘述了,大家可以用扩欧a一下P3811,P2613。



我要讲的讲完了,如果觉得讲的还好,请关注我的blog,谢谢

原文地址:https://www.cnblogs.com/mashiro-/p/9526323.html

时间: 2024-10-10 00:46:15

扩展欧几里得(exgcd)与同余详解的相关文章

扩展欧几里得 exGCD

Elementary Number Theory - Extended Euclid Algorithm Time Limit : 1 sec, Memory Limit : 65536 KB Japanese version is here Extended Euclid Algorithm Given positive integers a and b, find the integer solution (x, y) to ax+by=gcd(a,b), where gcd(a,b) is

exgcd扩展欧几里得求解的个数

知识储备 扩展欧几里得定理 欧几里得定理 (未掌握的话请移步[扩展欧几里得]) 正题 设存在ax+by=gcd(a,b),求x,y.我们已经知道了用扩欧求解的方法是递归,终止条件是x==1,y==0: int exgcd( int a, int b, int &x, int &y ) { if( b == 0 ) { x = 1; y = 0; return a; } int tmp = a % b; if( tmp > b ) swap( tmp, b ); int ans=exg

POJ 1061 青蛙的约会 扩展欧几里得

扩展欧几里得模板套一下就A了,不过要注意刚好整除的时候,代码中有注释 #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; typedef long long ll; ll exgcd(ll a, ll b, ll&x, ll&y) { if (b ==

POJ 2115 (模线性方程 -&gt; 扩展欧几里得)

题意: for(i=A ; i!=B ;i +=C)循环语句,问在k位操作系统中循环结束次数. 若在有则输出循环次数. 否则输出死循环. 存在这样的情况:i= 65533 :i<=2:i+= 4:时i = 2: 由模线性方程->扩展欧几里得 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <queue> using

POJ 2115 C Looooops(扩展欧几里得应用)

题目地址:POJ 2115 水题..公式很好推.最直接的公式就是a+n*c==b+m*2^k.然后可以变形为模线性方程的样子,就是 n*c+m*2^k==b-a.即求n*c==(b-a)mod(2^k)的最小解.(真搞不懂为什么训练的时候好多人把青蛙的约会都给做出来了,这题却一直做不出来.....这两道不都是推公式然后变形吗.....) 代码如下: #include <iostream> #include <cstdio> #include <string> #incl

【Luogu】P1516青蛙的约会(线性同余方程,扩展欧几里得)

题目链接 定理:对于方程\(ax+by=c\),等价于\(a*x=c(mod b)\),有整数解的充分必要条件是c是gcd(a,b)的整数倍. --信息学奥赛之数学一本通 避免侵权.哈哈. 两只青蛙跳到一格才行,所以说 \(x+mt=y+nt(mod l) \) \((x-y)+(m-n)t=0(mod l)\) \((m-n)t+ls=(y-x)  s属于整数集\) 令a=n-m,b=l,c=gcd(a,b),d=x-y 则有\( at+bs=d\) 扩展欧几里得求解. 设c=gcd(a,b)

poj1061--青蛙的约会--扩展欧几里得

Description 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置.不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的.但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的.为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面. 我们把这

扩展欧几里得定理

扩展欧几里得定理,很早之前就接触过,没看懂,放弃了,前些天有个有一个题,用扩展欧几里得定理,我竟然都不知道.决定看一下. 看扩展欧几里得定理的最好地方是用维基百科搜索扩展欧几里得算法 就能搜到 照着上面提供第例子走一遍知道什么意思了. 反正主要就是 ax+by=1(mod n) 的x,y值: 还有就是 ax=b(mod n) 这类题的通用解法: 下面粘上一个例子,看完这个例子你就明白了. poj 1061 青蛙的约会 中文题目,我的最爱. 我们设他们跳的步数为step 那么就可以写出算式 n*s

UVA 12169 Disgruntled Judge 枚举+扩展欧几里得

题目大意:有3个整数 x[1], a, b 满足递推式x[i]=(a*x[i-1]+b)mod 10001.由这个递推式计算出了长度为2T的数列,现在要求输入x[1],x[3],......x[2T-1], 输出x[2],x[4]......x[2T]. T<=100,0<=x<=10000. 如果有多种可能的输出,任意输出一个结果即可. 由于a和b都小于等于10000,直接枚举a和b暴力可以过.但是有没有更快的方法呢? 首先令递推式的i=2,那么x[2]=(a*x[1]+b)mod 1