定义:a,b是整数,形如ax≡b(mod m)ax≡b(mod m),且x是未知整数的同余式称为一元线性同余方程。
定理:a,b,m是整数且m>0,gcd(a,m)=dgcd(a,m)=d,如果d|b,则方程恰好有d个模m不同余的解,否则方程无解。
由同余方程的定义式可得ax+my=bax+my=b,这个方程称为二元一次不定方程。
-
解一元线性同余方程
设d=gcd(a,m),由定理可知若不满足d|b,那么方程无解;否则:
a=d∗a0a=d∗a0
m=d∗m0m=d∗m0
那么方程变为:a0x+m0y=b/d(二元一次不定方程两边同除以d)a0x+m0y=b/d(二元一次不定方程两边同除以d)
由于此时gcd(a0,m0)=1gcd(a0,m0)=1,因此可以运用扩展欧几里得算法得出方程a0x+m0y=b/da0x+m0y=b/d的解x(等价于a0x(b/d)+m0y(b/d)=1)(等价于a0x(b/d)+m0y(b/d)=1),
虽然x不唯一,但是属于一个模m剩余系,由定理可知,共有d个模m剩余类满足方程,其代表分别为:(由a0x≡bd(mod m0)得到a0x≡bd(mod m0)得到)
x,x+m0,x+2m0...x+(d−1)m0x,x+m0,x+2m0...x+(d−1)m0
-
例题(POJ 1061青蛙的约会)
- 题目大意
在一个圆环上有两只青蛙A和B,从0点自东向西为正方向,两只青蛙的位置分别为x,y,A每次跳m,B每次跳n,环总长为L.两只青蛙同时出发,两只青蛙落在同一点视为相遇,问最少经过几次跳跃两只青蛙相遇。
- 分析
设k次相遇,则有x+mk≡y+nk(mod L)x+mk≡y+nk(mod L)
转化成(m−n)k≡(y−x)(mod L)(m−n)k≡(y−x)(mod L)就变成了一个裸的线性同余方程的问题了
- 代码
-
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #include<cstdlib> #include<queue> using namespace std; typedef long long int LL; LL Gcd(LL a,LL b) { return b==0?a:Gcd(b,a%b); } LL Exgcd(LL a,LL b,LL &x,LL &y) { if(b==0) { x=1; y=0; return a; } else { LL ans=Exgcd(b,a%b,x,y); LL temp=x; x=y; y=temp-(a/b)*y; return ans; } } //这种写法方便理解 LL f(LL a,LL b,LL m)//求解一元线性同余方程 { LL x,y,d; LL a0,m0; d=Gcd(a,m); if(b%d!=0)return -1; a0=a/d; m0=m/d; Exgcd(a0,m0,x,y); x=x*b/d; x=(x%m0+m0)%m0; return x; } //这种写法更简洁 LL f2(LL a,LL b,LL m)//求解一元线性同余方程 { LL x,y,d; d=Exgcd(a,m,x,y); if(b%d!=0)return -1; x=x*(b/d)%m; x=(x%(m/d)+(m/d))%(m/d); return x; } int main() { LL x,y,m,n,L; LL A,B,C,xx,yy,d; while(scanf("%I64d %I64d %I64d %I64d %I64d",&x,&y,&m,&n,&L)!=EOF) { LL ans=f2(n-m,x-y,L); if(ans==-1)cout<<"Impossible"<<endl; else cout<<ans<<endl; } return 0; }
-
感谢大佬!