20191010 专题:扩展欧几里得

总览:

一、辗转相除求最大公约数:

int gcd(int x,int y)
{
	if(y==0)	return x;
	else	return gcd(y,x%y);
}

二、裴蜀定理:

对任何整数 a、b 和它们的最大公约数 d,关于未知数 x 和 y 的线性不定
方程:ax+by=c 有解(当且仅当 c 是 d 的倍数)。
该方程有解,必然有无穷多个解,因为若(x0,y0)是一组解,那(x0+kb,y0-ka)也是原方程的解(其中 k 为整数)。

推论:ax+by=1 有解的充分必要条件是 a 和 b 互质,即 gcd(a,b)=1。
x 在[0,b]范围有唯一解。

T1 P4549 【模板】裴蜀定理

洛谷P4549
题目描述
给出n个数(A1…An)现求一组整数序列(X1…Xn)使得S=A1X1+…AnXn>0,且S的值最小

输入格式
第一行给出数字N,代表有N个数 下面一行给出N个数

输出格式
S的最小值

输入输出样例
输入 #1
2
4059 1782
输出 #1
99
说明/提示
对于100%的数据, 1 ≤ n ≤ 20 , ∣ x i ∣ ≤ 100000. 1 \le n \le 20,|x_i| \le 100000. 1n20xi100000.

思路:即求最大公因数

代码:

#include<bits/stdc++.h>
using namespace std;

int n,a[25];
int gcd;

int gcdex(int x,int y)
{
	if(y==0)	return x;
	else	return gcdex(y,x%y);
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
		gcd=gcdex(gcd,a[i]);
	printf("%d",abs(gcd));
	return 0;
}

三、解方程:

预备:求解ax≡c(mod b)即求解ax+by=c
1.已知 a,b,先求出 ax+by=gcd(a,b)的一组解。

算法分析:
预备知识:① gcd(a,b)=gcd(b,a%b);② a%b=a-a/b*b
我们不妨设 a > b a > b a>b
(1)显然当 b = 0 , g c d ( a , b ) = a b=0,gcd(a,b)=a b=0gcd(a,b)=a。此时 x = 1 , y = 0 x=1,y=0 x=1y=0;
(2) a ∗ b ! = 0 a*b!=0 ab!=0
a x 1 + b y 1 = g c d ( a , b ) ax1+by1=gcd(a,b) ax1+by1=gcd(a,b)
b x 2 + ( a % b ) y 2 = g c d ( b , a % b ) bx2+(a\%b)y2=gcd(b,a\%b) bx2+(a%b)y2=gcd(b,a%b)
因为 g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)
则: a x 1 + b y 1 = b x 2 + ( a % b ) y 2 ax1+by1=bx2+(a\%b)y2 ax1+by1=bx2+(a%b)y2
因为 a % b = a − a / b ∗ b a\%b=a-a/b*b a%b=aa/bb
即: a x 1 + b y 1 = b x 2 + ( a − ( a / b ) ∗ b ) y 2 = b x 2 + a y 2 − ( a / b ) ∗ b y 2 ax1+by1=bx2+(a-(a/b)*b)y2=bx2+ay2-(a/b)*by2 ax1+by1=bx2+(a(a/b)b)y2=bx2+ay2(a/b)by2
a x 1 + b y 1 = a y 2 + b ( x 2 − ( a / b ) ) y 2 ; ax1+by1=ay2+b(x2-(a/b))y2 ; ax1+by1=ay2+b(x2(a/b))y2;
根据恒等定理得: x 1 = y 2 ; y 1 = x 2 − ( a / b ) ∗ y 2 ; x1=y2 ; y1=x2-(a/b)*y2; x1=y2;y1=x2(a/b)y2;
这样我们就得到了求解 x 1 , y 1 x1,y1 x1,y1 的方法: x 1 , y 1 x1,y1 x1y1的值可以通过求 x 2 , y 2 x2,y2 x2y2得到,以此类推……
显然上面的思想是递归定义,因为 gcd 不断的递归求解一定会有个时候 b=0,这是递归结束结束的边界。
举例: 15 x + 9 y = 3 15x+9y=3 15x+9y=3,通过如上过程的推导, a , b , x , y a,b,x,y abxy 在不同时刻的值如下:
在这里插入图片描述
注意:a 和 b 的值是从上往下推算,而 x 和 y 的值是从下往上倒推出来的。

2.求出方程 ax+by=c 的解

现在已经求出方程 a ∗ x + b ∗ y = g c d ( a , b ) a*x+b*y=gcd(a,b) ax+by=gcd(a,b)的解为 x 0 和 y 0 x_0和 y_0 x0y0,那如何求下面方程的解 x 和 y x 和 y xy 呢?
a x + b y = ax+by= ax+by=c,其中 c c c g c d ( a , b ) gcd(a,b) gcd(a,b)若干倍
我们将求解 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)的结果的再乘以 c / g c d ( a , b ) c/gcd(a,b) c/gcd(a,b)就好了,即:
x = x 0 ∗ c / g c d ( a , b ) x=x_0*c/gcd(a,b) x=x0c/gcd(a,b)
5、求出方程 a x + b y = c ax+by=c ax+by=c 最小的解
定理 1:若 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1,则方程 a x ≡ c ( m o d b ) 在 [ 0 , b − 1 ] ax≡c(mod b)在[0,b-1] axc(modb)[0,b1]上有唯一解。
定理 2:若 g c d ( a , b ) = d gcd(a,b)=d gcd(a,b)=d,则方程 a x ≡ c ( m o d b ) 在 [ 0 , b / d − 1 ] ax≡c(mod b)在[0,b/d-1] axc(modb)[0,b/d1]上有唯一解。
如果得到 a x ≡ c ( m o d b ) ax≡c(mod b) axc(modb)的某一特解 X X X,那么我令 r = b / g c d ( a , b ) r=b/gcd(a,b) r=b/gcd(a,b),可知 x x x [ 0 , r − 1 ] [0,r-1] [0,r1]上有唯一解,所以我用 x = ( X x=(X%r+r)%r x=(X 就可以求出最小非负整数解 x x x 了!
因为 X % r X\%r X%r 可能是负值,此时保持在 [ − ( r − 1 ) , 0 ] [-(r-1),0] [(r1),0]内,正值则保持在 [ 0 , r − 1 ] [0,r-1] [0,r1]内,那么加上 r r r就保持在 [ 1 , 2 r − 1 ] [1,2r-1] [1,2r1]内,所以再模一下 r r r 就在[ 0 , r − 1 ] 0,r-1] 0,r1]内了。

T2 P1082 同余方程

洛谷P1082
题目描述
求关于 xx的同余方程 a x ≡ 1 ( m o d b ) ax≡1(modb) ax1(modb)的最小正整数解。

输入格式
一行,包含两个正整数a,b,用一个空格隔开。

输出格式
一个正整数 x 0 x_0 x0,即最小正整数解。
输入数据保证一定有解。

输入输出样例
输入 #1
3 10
输出 #1
7

说明/提示
【数据范围】

对于 40%的数据, 2 ≤ b ≤ 1 , 0002 ≤ b ≤ 1 , 000 2 ≤b≤ 1,0002≤b≤1,000 2b1,0002b1,000

对于 60%的数据, 2 ≤ b ≤ 50 , 000 , 0002 ≤ b ≤ 50 , 000 , 000 2 ≤b≤ 50,000,0002≤b≤50,000,000 2b50,000,0002b50,000,000

对于 100%的数据, 2 ≤ a , b ≤ 2 , 000 , 000 , 0002 ≤ a , b ≤ 2 , 000 , 000 , 000 2 ≤a, b≤ 2,000,000,0002≤a,b≤2,000,000,000 2a,b2,000,000,0002a,b2,000,000,000

NOIP 2012 提高组 第二天 第一题

思路:板子题

程序:

#include<bits/stdc++.h>
using namespace std;

int a,b;

int gcdex(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	int gcd=gcdex(b,a%b,x,y),h;
	h=x-a/b*y;
	x=y;
	y=h;
	return gcd;
}

int main()
{
	scanf("%d%d",&a,&b);
	int x,y,gcd;
	gcd=gcdex(a,b,x,y);
	int mod=b/gcd;
	printf("%d",(x%mod+mod)%mod);
	return 0;
}

T3 P1516 青蛙的约会

洛谷P1516
题目描述
两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。

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

其中 0 < x ≠ y < = 2000000000 , 0 < m 、 n < = 2000000000 , 0 < L < = 2100000000 。 0<x≠y < =2000000000,0 < m、n < =2000000000,0 < L < =2100000000。 0<x=y<=20000000000<mn<=20000000000<L<=2100000000

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

输入输出样例
输入 #1
1 2 3 4 5
输出 #1
4

思路:追及问题
距 离 : s = x − y , 速 度 差 : v = n − m ( 注 意 反 减 ) 距离:s=x-y,速度差:v=n-m(注意反减) s=xyv=nm
列 方 程 v ∗ x − L ∗ y = s ( 路 程 差 ( 速 度 差 乘 时 间 ) 减 去 若 干 圈 即 为 距 离 ) 列方程v*x-L*y=s(路程差(速度差乘时间)减去若干圈即为距离) vxLy=s
解方程即可

程序:

#include<bits/stdc++.h>
#define LL long long
using namespace std;

LL u,v,m,n,l;

LL gcdex(LL a,LL b,LL &x,LL &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	LL gcd=gcdex(b,a%b,x,y),h;
	h=x-(a/b)*y;
	x=y;
	y=h;
	return gcd;
}

int main()
{
	scanf("%lld%lld%lld%lld%lld",&u,&v,&m,&n,&l);
	LL a=m-n,b=l,x,y,s=v-u;
	if(a<0)
	{
		a=-a;
		s=-s;
	}
	LL gcd=gcdex(a,b,x,y);
	if(s%gcd!=0)	printf("Impossible");
	else
	{
		int r=b/gcd;
		printf("%lld",(x*(s/gcd)%r+r)%r);
	}	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值