线性方程 ax +by =gcd(a,b)的解:
假设 特解(x0,y0) 是方程组的一组解,d=gcd(a,b),那么通解就是 x =x0+b/d*k , y=y0-a/d*k;
例如 10x+35y =5,的一组特解(-3,1),那么通解可表示为(-3+7*k,1-2*k),带入之后显然满足方程,那么我们就可以构造出无穷无尽组解符合原来的方程。
1.gcd函数既满足交换率也满足结合律:
gcd(a,b,c)=gcd(a,b-a,c-b);
证明: 假设 d=gcd(a,b,c),那么 d|a,d|b,d|c => d|(b-a) , d|(c-a) 所以d 是 (a,b-a,c-a) 的一个公约数 那么d<=gcd(a,b-a,c-b) => gcd(a,b,c)<=gcd(a,b-a,c-b);
假设 d1=gcd(a,b-a,c-b),那么 有 d1 |a,d1 |(b-a),d1|(c-b) => d1(b-a+a)=> d1|b 同理 d1|c ,所以d1是 a,b,c 的一个公约数,所以 d1<=gcd(a,b,c) => gcd(a,b,c)>=gcd(a,b-a,c-b);
所以 gcd(a ,b-a ,c-b)=gcd(a,b,c) 得证。
同余 :如果说 a 、b 除以m 的余数相同,那么说 a 与 b 模 m 同余,记作 a≡b(m)。
整除符号是 “ | ”
费马小定理: 如果p 是质数 那么对于任意的正整数 a 都有 a^p≡a(p) 也就是说 如果a 是质数那么a的任意整数幂 与 a 模p 同余 。
乘法逆元的定义:
如果说a与b互质且b能够整除a 那么存在一个整数x 使得a/b ≡a*x(m) 则称x 为b 的模 m 的乘法逆元。记作b逆(m);(如果说b ,m 不是互质的,乘法逆元不存在);
首先我们用p代表模数m.
证明 1:当p 是质数是 b逆=b ^(p-2);
有乘法逆元的定义可知 a/b≡a*b逆(p)两边同是乘以b可得 a≡a*b*b逆(p)
整理得 b*b逆≡1(p)因为p是质数满足费马小定理使用条件则,b^p≡b(p);
先把两边同时乘以b 得 b^2*b逆≡b(p)=> b^2*b逆≡b^p(p);
此时两边在同除以b^2;
得b逆=b^(p-2);
此时借助据快速幂就可以在log(p)的时间复杂度内求出逆元
快速幂求逆元:
//情景:给定 n 组 ai,pi,其中 pi 是质数,求 ai 模 pi 的乘法逆元,若逆元不存在则输出 impossible。
/*什么时候无解呢,当a是p的倍数是,一定是无解的,因为当a是p的倍数时,a%p==0定义时就不满足了,除此之外,我们都可以用费马小定理构造出逆元*/
#include
using namespace std;
#define int long long
int qp(int a,int b,int p){
int res=1;
for(;b;b>>=1){
if(b&1) res=(res*a)%p;
a=(a*a)%p;
}
return res;
}
signed main(){
int tt;
cin>>tt;
while(tt--){
int a,p;
cin>>a>>p;
int res=qp(a,p-2,p);
if(a%p==0)cout<<"impossible"<
推理二 :如果仅保证b ,m 互质的情况下乘法逆元可以通过求解同余方程 b*x≡1(p)的解。
如果我们在计算过程中遇到了a/b这样的分式计算也可以先把a,b同时对p 取模 再把 a*b逆%p 作为计算的结果,是结果变成一个整数,当然这样做的前提是b 与 p 是互质的,一般题目会强调这一点。
那么现在问题就来到了如何求解同余方程了:
线性方程的引入:
裴属定理:对于任意一对正整数a,b ,一定存在整数x,y使得ax +by =Gcd(a,b);
证明:因为a 是最大公约数的倍数,b 是最大公约数的倍数 ,所以(a*x+b*y)也是最大公约数的倍数。
ex_gcd拓展欧几里得算法:就是解决上述方程的算法。
顾名思义,拓展欧几里得算法的老爹就是欧几里得算法
1) 欧几里得算法:求a与 b的最大公约数 GCD ;
证明1:gcd(a,b)=gcd(b,a%b);
“|” 为整除符号
推论1:设d为a,b的最大公约数,则有d |a,d | b ,那么一定有 d |(x*a+y*b);
反过来如果一个数及能整除a又能整除b那么 这个数一定是a与b的公约数。
因为p|a,p|b,因为a%b=a-a/b*b 我们把a/b看作常数c 那么a%b=a-c*b
所以根据推论1:p|(a-c*b)就是说 p|(a%b);
也就是说,p也是a与a%b 的公约数,所以 gcd(a,b)=gcd(b,a%b)
因为a%b
特别的gcd(0,a)=a;
2)再来看看拓展欧几里得算法:
若要求线性方程 :ax +by =Gcd(a,b):
设d 为a,b的最大公约数;
1>当b 等于零时: x=1,y=0,顺便带出gcd(a,0)=a;
2>当b 不等于零时:
有欧几里得算法gcd(a,b)=gcd(b,a%b)=d 可知 (a%b)*x+b*y=d;
由于 取模性质 a%b =a-a/b *b (除法默认下取整)
那么 (a-a/b) x+b*y=d => ax+b(y-ax/b )=d (略去乘号)
化简得 a*x +b*(y-a/b *x)=d;观察一下 这不就是 ax+by=d 的变形吗。
x不变,我们只需把 (y-a/b*x)看作 y 相当于递归求解 线性方程 ax +by =Gcd(a,b)由于 y递减,最中一定可以得到答案。
因此可以采用递归算法先求出下一层的x和y 在利用上述公式回代即可;
//给定 n 对正整数 ai,bi,对于每对数,求出一组 xi,yi,使其满足 ai×xi+bi×yi=gcd(ai,bi)。
//
//输入格式
//第一行包含整数 n。
//
//接下来 n 行,每行包含两个整数 ai,bi。
#include
using namespace std;
int exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
signed main(){
int tt;
cin>>tt;
while(tt--){
int a,b,x,y;
cin>>a>>b;
exgcd(a,b,x,y);
cout<
给定 n 组数据 ai,bi,mi,对于每组数求出一个 xi,使其满足 ai×xi≡bi(modmi),如果无解则输出 impossible。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一组数据 ai,bi,mi。
输出格式
输出共 n 行,每组数据输出一个整数表示一个满足条件的 xi,如果无解则输出 impossible。
每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。
输出答案必须在 int 范围之内。
数据范围
1≤n≤e5,
1≤ai,bi,mi≤2×e9
输入样例:
2
2 3 6
4 3 5
输出样例:
impossible
-3
启发:该问题可以转变成一个乘法逆元问题;
根据同余方程的性质:
ai *xi ≡bi (mod mi) => ai*xi=mi*y+bi => ai*xi-mi*y=bi => ai*xi+mi(-y)=bi;
对于每组 样例求解 ax+m (-y)=b;
这样就完美的把一个同余方程转化成了线性方程,但这还不够。
我们如果想利用拓展欧几里得算法求解线性方程ax +by =Gcd(a,b)
必须把等式的右边化成 gcd(a,b);
我们设 d=gcd(a,b);
于是我们想到 a*(x*d/b)+m*(-y*d/b)=b*d/b =>从而 a*(x*d/b)+m*(-y*d/b)=d;
正是因为这个变形,等式变得不一定有解了,本来根据裴属定理,如果x ,y 都是整数,方程是一定有整数解的,现在经过变形,对应原方程类x,类y 就不是整数了,线性方程的解就和 b%d 有关了,这是因为只有b%d==0,x*d/b、y*d/b 才会是整数,会有整数解,否则才就算扩大若干倍答案总会出现非整数。
我们用拓展欧几里得算法求解出这个线性方程组的解 就是 x*d/b
把这个解乘以 b/d 就是真正的x 的值了
为了方便简化方程为 ax+my’=b ,只要b能整出除d,那么 方程一定有整数解。
注:题目中并没有问我们y 的取值,我们就无需理会y 的符号所以这个程序把 -y 直接看成y 这对结果x的值没有影响,此外还用到了整数取模公式,为了避免爆int 开了long long
#include
using namespace std;
#define int long long
int exgcd(int a,int b,int &x,int &y){
if(!b) {
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
signed main(){
int tt;
cin>>tt;
while(tt--){
int a,b,m;
cin>>a>>b>>m;
int x,y;
int d=exgcd(a,m,x,y);
if(b%d) puts("impossible");
else cout<<(b/d*x%m+m)%m<