拓展欧几里得算法——C.一日之计在于晨

广州大学第十八届ACM大学生程序设计竞赛(同步赛)C题

谈到拓展欧几里得算法,就要从欧几里得算法裴蜀定理说起。

欧几里得算法(辗转相除法)

gcd(a,b)=gcd(b,amodb)

其中,当a=0时,gcd(a,b)=gcd(0,b)=bb=0同理

证明gcd(a,b)=gcd(b,ab)

a=b+c, gcd(a,b)=d, gcd(b,c)=e
主要是证明d=e
amodd=0,bmodd=0,cmodd=0,de

同理,ed,d=e

推广一下,相当于a不断减b直至a<b,也就是直至amodb

int gcd(int a,int b){
	if(b==0)return a;
	return gcd(b,a%b);
}

参考:
辗转相除法的原理 _辗转相除法求最大公约数的原理是什么? (shadafang.com)

裴蜀定理(贝祖定理)

a,b是不全为0的整数,对任意整数x,y,满足gcd(a,b)|ax+by,且存在整数x,y,使得ax+by=gcd(a,b)

首先证明任意整数x,y,满足gcd(a,b)|ax+by

gcd(a,b)=d,a=k1d,b=k2d,(k1,k2)=1
ax+by=k1dx+k2dy=d(k1x+k2y)
gcd(a,b)|ax+by

再证明存在整数x,y,使得ax+by=gcd(a,b)

设取整数 x0,y0 时,ax+by 有最小正整数s,即ax0+by0=s
ax0modgcd(a,b)=0
by0modgcd(a,b)=0
smodgcd(a,b)=0

a=qs+r,(0r<s) ---也就是把a表示成一个整数,其中s为已知,qr为未知数
r=aqs=aq(ax0+by0)=a(1qx0)+b(qy0)=ax+by ---r可以表示成 ax+by 的形式
sax+by 的最小正整数
r=0
a=qsamods=0
同理,bmods=0
gcd(a,b)mods=0

smodgcd(a,b)=0,gcd(a,b)mods=0,gcd(a,b)=s

拓展欧几里得算法

拓展欧几里得算法实际上就是ax+by=gcd(a,b) 的一组整数解

b=0时,ax+by=ax=gcd(a,0)=a
此时,x=1,y=0 就是 ax+by=gcd(a,b) 的一组整数解

b0时,
欧几里得算法可知,gcd(a,b)=gcd(b,amodb)
裴蜀定理可知,

gcd(a,b)=ax+bygcd(b,amodb)=bx1+(amodb)y1

x=y1,y=x1aby1
利用递归,先求出下一层的x1,y1,以此类推,可以求得特解(x0,y0)
构造通解

{x=x0+bgcd(a,b) ky=y0agcd(a,b) k

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的一组整数解

拓展欧几里得算法可知,我们可以求ax+by=gcd(a,b)的一组整数解
那么,如果 cmodgcd(a,b)=0 ,我们只需在等式左右两边同乘c/gcd(a,b) ,就能求得 ax+by=c的一组整数解

cmodgcd(a,b)0,则无整数解。这个可以有裴蜀定理可知,证明对于不定方程 ax+by=m 其有整数解的充要条件是 gcd(a,b)|m 即可。

例题

广州大学第十八届ACM大学生程序设计竞赛(同步赛)C题

题目

题解

根据指针的转动,可以列出 (只是大致的,未考虑加一/减一)t+ax=re+my其中,x表示顺时针或逆时针转动的次数,re 表示指针最后停的位置,y 表示转动的圈数

移动,axmy=ret
我们就可以根据拓展欧几里得算法求得 gcd(a,m),以及对应的 (x0,y0)
ret 是当re取最小值时,gcd(a,m)|ret
re的范围是[1,m],所以 re=(t1)%d+1
这样就可以求得特解x=x(ret)/d)(后面还要加mod)
最后再根据通解,求出 x 的最小值。

代码

#include <bits/stdc++.h>
#define int long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
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;

}
void solve(){
    int t,a,m;
    cin>>t>>a>>m;
    int x,y;
    int d=exgcd(a,m,x,y);
    t=(t-1)%d+1-t; //这里就省略了re
    x=((x*t/d)%m+m)%m; //特解
    x=(x%(m/d)+(m/d))%(m/d); //通解
    cout<<min(x,m/d-x)<<endl;
}
signed main()
{
    ios;
    int T;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
posted @   菜dog的日常生活  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示