常用基本算法1(扩欧,同余方程)

欧几里得算法

1.最大公约数与欧几里得算法

欧几里得算法

gcd(a,b)=gcd(b,a%d)

证明:
有个十分简洁优美的证明。不妨设 a>b。我们的思路是,证明 a,b 的所有公因子,与 b 和(a – b)的公因子是完全相同的两堆数!那么最大的公因子也相等了!
st1:
求证gcd(a,b)=gcd(b,a-b)
容易证明若x|a,x|b,则x|(a±b)。
记S1为gcd(a,b)的解集,S2为gcd(b,a-b)的解集
if x 属于S1,x|a且x|b,so x|(a-b) 得S1属于S2 if x 属于S2,x|(a-b)且x|b,so x|a 得S2属于S1。
all in all S1=S2;
so gcd(a, b) = max(S1) = max(S2) = gcd(b, a – b)
st2:
同理 gcd(a,b)=gcd(b,a-kb)=gcd(b,a%b);
最大公因数的一些性质:
① gcd(a, ka) = |a|
② 如果 d|a, d|b,则 d|gcd(a, b)
③ gcd(an, bn) = n gcd(a, b)
④ 如果 d|ab 且 gcd(a, d)=1,则 d|b
⑤ 如果 d|a 且 d |/ b,则 gcd(a, b)=gcd(a/d, b)
⑥ gcd(N–1, N)=1;(因为 gcd(N – 1, N) = gcd(N, 1) = 1)
gcd(N–p, N)≤p (因为 gcd(N–p, N)=gcd(N, p)≤p)
⑦ lcm(a, b) = a×b / gcd(a, b)
luogu Hankson 的趣味题
设某未知正整数 x满足:
1. x 和 a0 的最大公约数是 a1;
2. x 和 b 0 的最小公倍数是 b1。
Hankson 的“逆问题”就是求出满足条件的正整数x。
gcd(x,a0)=a1
x=a1k1,a0=a1k2 ,
gcd(k1,k2)=1;(证明:若gcd(k1,k2)=t!=1 gcd(x,a0)=a1/t;)so gcd(x/a1,a0/a1)=1;
lcm(x,b0)=b1;so xb0/gcd(x,b0)=b1
gcd(x,b0)=x*b0/b1;同理gcd(b1/b0,b1/x)=1
x|b1,a1|x
然后枚举……

#include<cstdio>
#include<iostream>
using namespace std;
int n,a2,a1,b1,b2;
int gcd(int a,int b)
{
	return b==0?a:gcd(b,a%b);
}
int main()
{
	scanf("%d",&n);
	while(n--)
	{
		scanf("%d%d%d%d",&a1,&a2,&b1,&b2);
		int q=a1/a2,p=b2/b1,ans=0;
		for(int x=1;x*x<=b2;x++)
		 if(b2%x==0)
	     {
	     	if(x%a2==0&&gcd(x/a2,q)==1&&gcd(p,b2/x)==1)
	     	ans++;
	     	int y=b2/x;
	     	if(x==y) continue;
	     	if(y%a2==0&&gcd(y/a2,q)==1&&gcd(p,b2/y)==1)
	     	ans++;
		 }
		cout<<ans<<endl;
	}
}

2.扩展欧几里得

一个来求ax+by=c 的一组x,y解的东东
由前可得 bx1+(a%b)y1=c —>> bx1+(a-k*b)y1=c —>> ay1+b(x1-ky1)=c;
x=y1,y=x1-ky1;

递归……

终止条件 a%b=0 gcd(a,b)x+0y=c;
x1=c/gcd(a,b),y1=0可为一组解;
显然通解 x=x1+k
b/gcd(a,b) y=y1+k*a/gcd(a,b)

void exgcd(ll x,ll y,ll a,ll b,ll c)
{
	if(b==0){x=c/a; y=0; return;}
	else{ exgcd(x,y,b,a%b,c);ll t=x; x=y,y=t-a/b*y;}
}

PS:ax+by=c 若gcd(a,b)不能|c则方程无解——裴蜀定理

luogu 同余方程
求关于x 的同余方程 ax≡1(modb) 的最小正整数解。

#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
#define ll long long
using namespace std;
ll x,y,c;
void exgcd(ll a,ll b)
{
	if(b==0)
	{
		x=1/a;
		y=0;
		c=a;
	}
	else
	{
		exgcd(b,a%b);
		ll t=x;
		x=y;y=t-a/b*y;
	}
}
int main()
{
	ll a,b;
	scanf("%lld%lld",&a,&b);
	exgcd(a,b);
	cout<<(x%b+b)%b;//最小正整数的处理
}

3.同余方程

a≡b(mod n) 表示 a mod n = b mod n —>> n|(a–b)
★同余的性质:
A.
①自反性: a≡a(mod n)
②传递性:若 a≡b(mod n), b≡c(mod n) 则 a≡c(mod n)
③对称性:a≡b(mod n)  b≡a(mod n)
缩写:a1≡a2≡a3≡…≡ak (mod n)
B.
模算术运算:
若 a≡b(mod n),则 an≡bn(mod n),n 是整数
若 a≡b(mod n),c≡d(mod n), 则 a±c≡b±d(mod n),ac≡bd(mod n)
C.
若 d≥1, d|n,则 a≡b(mod n) —>> b≡a(mod d)
若 ac≡bc(mod n),则 a≡b(mod (n/gcd(c, n)) )
若 a≡b(mod n),则 ad≡bd(mod nd)
若 c, n 互质,ac≡bc(mod n),则 a≡b(mod n)
D.费马小定理(详见下方)
若 p 为质数,则 ap≡a(mod p)
若 p 为质数,且 a, p 互质,则 ap–1≡1(mod p)
求解线性同余方程
ax=b(mod c) 转化为ax+cy=b,进而用exgcd求通解
luogu 青蛙的约会
题面
两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面
分析
青蛙的约会
时间为t,长度为l,
A B
坐标 x y
跳距 m n
(x+tm)%l=(y+tn)%l
(x+tm)-(y+tn)=lP(P属于Z)
x-y+t(m-n)=lP so t(n-m)+lP=x-y 同属 ax+by=c
由扩展欧几里得求 t(n-m)+lP=gcd(n-m,P)(此时不能直接=x-y,because 不能保证有解情况)
得到 t0,t0为 c=gcd(n-m,P)时的特解
so t=t0*(x-y)/gcd(n-m,P)
why? ax+by=c (a/d)x+(b/d)y=c/d,d=gcd(x,y)
(a/d,b/d)=1;
so t通解=t+k(l/d);
why? (a,b)[a,b]=abs(ab);
q=[a,b] ax+kq+by-kq=ax+k(ab/d)+by-k(ab/d)
=a(x+k*(b/d))+b(y-k*(a/d));
为防止tmin为负数,tmin={(t0*(x-y)/d)%l/d+l/d}%(l/d)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#define ll long long
using namespace std;
ll x,y,m,n,l;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
	if(!b){d=a,x=1,y=0;}
	else{
		exgcd(b,a%b,d,x,y);
		int t=x;
		x=y,y=t-a/b*y;
		/*ax+by=c,bx+a%by=c
		  bx+(a-qb)y=c,ay+b(x-qy)=c,q=a/b*/
	}
}
int main()
{   ll a,b,d;
	cin>>x>>y>>m>>n>>l;
	if(n<m) swap(n,m),swap(x,y);
	exgcd(n-m,l,d,a,b);
	if((x-y)%d!=0||m==n) {cout<<"Impossible";return 0;}
    else cout<<((a*(x-y)/d)%(l/d)+l/d)%(l/d);
    return 0;
}

求解同余方程组
合并方程法我绝不告诉你我只会这个
如有方程组
x≡b1(modm1)
x≡b2(modm2)
由该方程组可设 x=k1m1+b1=k2m2+b2,
即 k1m1–k2m2=b2–b1 (1)
求出方程(1)的一组解(k1, k2),令 x3=k1m1+b1,m3=lcm(m1, m2)
(证明:k1t=k1+p m2/gcd(m1,m2)
x3=k1t m1+b1=k1 m1+p lcm(m1,m2)+b1,
so x=k1*m1+b1(mod lcm(m1,m2)))
则原方程组等价于方程 x≡x3(mod m3),相当于把两条方程合并成一条。方程组的多条方程也可以通过合并变成一条方程.
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值