欧几里得(扩展)算法详解

一、同余
1、一些普通的性质

1.反身性: a ≡ a ( m o d m ) a≡a (mod m) aa(modm)
2.对称性:若 a ≡ b ( m o d m ) a≡b(mod m) ab(modm),则 b ≡ a ( m o d m ) b≡a (mod m) ba(modm)
3.传递性:若 a ≡ b ( m o d m ) a≡b(mod m) ab(modm) b ≡ c ( m o d m ) b≡c(mod m) bc(modm),则 a ≡ c ( m o d m ) a≡c(mod m) ac(modm)
4.同余式相加(减):若 a ≡ b ( m o d m ) a≡b(mod m) ab(modm) c ≡ d ( m o d m ) c≡d(mod m) cd(modm),则 a + c ≡ b + d ( m o d m ) a+c≡b+d(mod m) a+cb+d(modm)
5.同余式相乘:若 a ≡ b ( m o d m ) , c ≡ d ( m o d m ) a≡b(mod m),c≡d(mod m) ab(modm)cd(modm),则 a c ≡ b d ( m o d m ) ac≡bd(mod m) acbd(modm)

2、一些同余式性质

1、同余式一边的数可以移到另一边,只要改变符号就可以了。

a + c ≡ b ( m o d m ) = > a ≡ b − c ( m o d m ) a+c≡b (mod m) => a≡b-c(mod m) a+cb(modm)=>abc(modm)

2、同余式的每一边都可以增加或减去模的任意倍数

a ≡ b ( m o d m ) = > a + k m ≡ b ( m o d m ) = > a − k m ≡ b ( m o d m ) a≡b(mod m) => a+km≡b(mod m) => a-km≡b(mod m) ab(modm)=>a+kmb(modm)=>akmb(modm)

3、同余式两边的数和模可以同时乘上一个整数。

a ≡ b ( m o d m ) = > k a ≡ k b ( m o d k m ) a≡b(mod m) => ka≡kb(mod km) ab(modm)=>kakb(modkm)

二、欧几里得算法(最大公约数gcd)
1、一些性质(证明过程简单了解就好)

g c d ( a , b ) = g c d ( b , a ) gcd(a,b)=gcd(b,a) gcd(a,b)=gcd(b,a)

g c d ( a , b ) = g c d ( a − b , b ) ( a ≥ b ) gcd(a,b)=gcd(a-b,b)(a\ge b) gcd(a,b)=gcd(ab,b)(ab)

g c d ( a , b ) = g c d ( a % b , b ) gcd(a,b)=gcd(a\%b,b) gcd(a,b)=gcd(a%b,b)

g c d ( a , b , c ) = g c d ( g c d ( a , b ) , c ) gcd(a,b,c)=gcd(gcd(a,b),c) gcd(a,b,c)=gcd(gcd(a,b),c)

g c d ( k a , k b ) = k   g c d ( a , b ) gcd(ka,kb)=k\ gcd(a,b) gcd(ka,kb)=k gcd(a,b)

2、求最大公约数模板
typedef long long ll;
ll gcd(ll a, ll b){
return b?gcd(b,a%b):a;
}

注意当 b ≠ 0 b\neq 0 b=0 时,返回值为 gcd(b,a%b) 而不是 gcd(a%b,b) ,否则会不断递归导致栈溢出(因为这样子的话b永远是b)

三、扩展欧几里得算法(重点)

、看这个二元一次不定方程
a x + b y = c ax+by=c ax+by=c
其中 a , b , c a,b,c a,b,c 是已知的正整数,如何求出方程的解呢?

  • 观察: 显然这可以看做一次函数,有无穷个实数解。不过我们研究的是整数解,这个方程有没有整数解就不一定了。

上述方程有解的充要条件是 g c d ( a , b ) ∣ c gcd(a,b)|c gcd(a,b)c c c c g c d ( a , b ) gcd(a,b) gcd(a,b) 的倍数)

可以理解为, g c d ( a , b ) gcd(a,b) gcd(a,b) a x + b y ax+by ax+by 可以表示出的最小整数。

贝祖定理(裴蜀定理):任意两个整数a,b,最大公约数为d=gcd(a,b),那么对于任意的整数x,y,ax+by=m,构成的m一定是d的整数倍(即m%d=0)

方程 a x + b y = d , d = g c d ( a , b ) ax+by=d,d=gcd(a,b) ax+by=d,d=gcd(a,b) 的所有解为
{ x = x 0 + b d t y = y 0 − a d t \begin{cases} x=x_0+\frac{b}{d}t\\ y=y_0-\frac{a}{d}t \end{cases} {x=x0+dbty=y0dat其中 x 0 , y 0 x_0,y_0 x0,y0 是一组特解,可以理解为任意解,只要有一组 x 0 , y 0 x_0,y_0 x0,y0,我们就能求出所有整数解(正负都可以)。

、方程 a x + b y = c ① , g c d ( a , b ) ∣ c ax+by=c①,gcd(a,b)|c ax+by=c,gcd(a,b)c 的所有解为
{ x = c d x 0 + b d t y = c d y 0 − a d t \begin{cases} x=\frac{c}{d}x_0+\frac{b}{d}t\\ y=\frac{c}{d}y_0-\frac{a}{d}t \end{cases} {x=dcx0+dbty=dcy0dat其中 x 0 , y 0 x_0,y_0 x0,y0 是方程 a x + b y = d , d = g c d ( a , b ) ax+by=d,d=gcd(a,b) ax+by=d,d=gcd(a,b) 的一组特解(注意,不是 a x + b y = c ax+by=c ax+by=c
看不懂的话可以这样解释:
说白了就是我们通过计算 a x ′ + b y ′ = g c d ( a , b ) ② ax'+by'=gcd(a,b)② ax+by=gcd(a,b)的解 ( x ′ , y ′ x',y' x,y因为和方程①的不一样,所以区别开来)然后方程两边同乘 c g c d ( a , b ) \frac{c}{gcd(a,b)} gcd(a,b)c,得到 a x ′ c g c d ( a , b ) + b y ′ c g c d ( a , b ) = c a\frac{x'c}{gcd(a,b)}+b\frac{y'c}{gcd(a,b)}=c agcd(a,b)xc+bgcd(a,b)yc=c,按照方程①,我们把那一坨换成 x , y x,y x,y即可。
{ x = c g c d ( a , b ) x ′ y = c g c d ( a , b ) y ′ \begin{cases} x=\frac{c}{gcd(a,b)}x'\\ y=\frac{c}{gcd(a,b)}y' \end{cases} {x=gcd(a,b)cxy=gcd(a,b)cy因此我们就能由 a x ′ + b y ′ = g c d ( a , b ) ② ax'+by'=gcd(a,b)② ax+by=gcd(a,b)的解求出 a x + b y = c ① ax+by=c① ax+by=c的解了。

扩展欧几里得算法模板:

事实上和欧几里得算法框架相同,不过递归过程中我们可以计算出 x , y x,y x,y

ll exgcd(ll a, ll b,ll &x,ll &y){
    if(!b){
        x=1;y=0;return a;
    }
    ll res=exgcd(b,a%b);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
    return res;
}

或者

ll extgcd(ll a,ll b,ll& x,ll& y){
    ll d = a;
    if (!b){
        x = 1;y = 0;
    }else{
        d = extgcd(b,a%b,y,x);//和欧几里得一样有递归
        y -= a/b*x;
    }
    return d;
}
洛谷 P1516 青蛙的约会

(广东工业大学ACM寒假集训 专题四 G)
专题学习链接https://vjudge.net/contest/479577#problem/G
题目描述
两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。

我们把这两只青蛙分别叫做青蛙 A 和青蛙 B,并且规定纬度线上东经 00 度处为原点,由东往西为正方向,单位长度 11 米,这样我们就得到了一条首尾相接的数轴。设青蛙 A 的出发点坐标是 xx,青蛙 B 的出发点坐标是 yy。青蛙 A 一次能跳 mm 米,青蛙 B 一次能跳 nn 米,两只青蛙跳一次所花费的时间相同。纬度线总长 LL 米。现在要你求出它们跳了几次以后才会碰面。

输入格式
输入只包括一行五个整数 x , y , m , n , L x,y,m,n,L x,y,m,n,L

输出格式
输出碰面所需要的天数,如果永远不可能碰面则输出一行一个字符串 Impossible

输入输出样例
在这里插入图片描述
说明/提示
100 % 100\% 100% 的数据, 1 ≤ x ≠ y ≤ 2 × 1 0 9 1 \le x \ne y \le 2 \times 10^{9} 1x=y2×109 1 ≤ m , n ≤ 2 × 1 0 9 1 \le m, n \le 2 \times 10^{9} 1m,n2×109 1 ≤ L ≤ 2.1 × 1 0 9 1 \le L \le 2.1 \times 10^{9} 1L2.1×109

简要分析:

如果青蛙跳的长度为无限长,那么我们相当于要解 x + k m = y + k n x+km=y+kn x+km=y+kn这个方程。
如果算上了 L L L
就是 x + k m ≡ y + k n ( m o d L ) x+km≡y+kn(mod L) x+kmy+kn(modL)

移一下 k ( m − n ) ≡ y − x ( m o d L ) k(m-n)≡y-x(mod L) k(mn)yx(modL)

k k k未知, ( m − n ) (m-n) (mn)已知, ( y − x ) (y-x) (yx)已知。

可以化为 k ( m − n ) + t L = y − x k(m-n)+t L=y-x k(mn)+tL=yx

a = m − n ; c = y − x ; b = L a=m-n;c=y-x;b=L a=mn;c=yx;b=L

就变成了我们熟悉的 a x + b y = c ① ax+by=c① ax+by=c

如果 a = m − n < 0 a=m-n<0 a=mn<0,我们就要让 a = − a , c = − c a=-a,c=-c a=a,c=c

#include<iostream>
typedef long long ll;
using namespace std;
ll x,y;
ll exgcd(ll a, ll b){
    if(!b){
        x=1;
        y=0;
        return a;
    }
    ll res=exgcd(b,a%b);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
    return res;
}
int main()
{
    ll xx,yy,n,m,l;
    cin>>xx>>yy>>m>>n>>l;
    ll a=m-n<0?n-m:m-n;
    ll c=m-n<0?xx-yy:yy-xx;
    ll b=l;
    ll gcd=exgcd(a,b);
    //cout<<a<<' '<<b<<' '<<c<<' '<<x<<' '<<gcd<<' ';
    if(c%gcd!=0)cout<<"Impossible";
    else {
        //x=x*(c/gcd);
        cout<<(x*(c/gcd)%(b/gcd)+b/gcd)%(b/gcd);
    }
}
  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值