总览:
一、辗转相除求最大公约数:
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.
1≤n≤20,∣xi∣≤100000.
思路:即求最大公因数
代码:
#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=0,gcd(a,b)=a。此时
x
=
1
,
y
=
0
x=1,y=0
x=1,y=0;
(2)
a
∗
b
!
=
0
a*b!=0
a∗b!=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=a−a/b∗b
即:
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
x1,y1的值可以通过求
x
2
,
y
2
x2,y2
x2,y2得到,以此类推……
显然上面的思想是递归定义,因为 gcd 不断的递归求解一定会有个时候 b=0,这是递归结束结束的边界。
举例:
15
x
+
9
y
=
3
15x+9y=3
15x+9y=3,通过如上过程的推导,
a
,
b
,
x
,
y
a,b,x,y
a,b,x,y 在不同时刻的值如下:
注意:a 和 b 的值是从上往下推算,而 x 和 y 的值是从下往上倒推出来的。
2.求出方程 ax+by=c 的解
现在已经求出方程
a
∗
x
+
b
∗
y
=
g
c
d
(
a
,
b
)
a*x+b*y=gcd(a,b)
a∗x+b∗y=gcd(a,b)的解为
x
0
和
y
0
x_0和 y_0
x0和y0,那如何求下面方程的解
x
和
y
x 和 y
x和y 呢?
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=x0∗c/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]
ax≡c(modb)在[0,b−1]上有唯一解。
定理 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]
ax≡c(modb)在[0,b/d−1]上有唯一解。
如果得到
a
x
≡
c
(
m
o
d
b
)
ax≡c(mod b)
ax≡c(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,r−1]上有唯一解,所以我用
x
=
(
X
x=(X%r+r)%r
x=(X 就可以求出最小非负整数解
x
x
x 了!
因为
X
%
r
X\%r
X%r 可能是负值,此时保持在
[
−
(
r
−
1
)
,
0
]
[-(r-1),0]
[−(r−1),0]内,正值则保持在
[
0
,
r
−
1
]
[0,r-1]
[0,r−1]内,那么加上
r
r
r就保持在
[
1
,
2
r
−
1
]
[1,2r-1]
[1,2r−1]内,所以再模一下
r
r
r 就在[
0
,
r
−
1
]
0,r-1]
0,r−1]内了。
T2 P1082 同余方程
(洛谷P1082)
题目描述
求关于 xx的同余方程
a
x
≡
1
(
m
o
d
b
)
ax≡1(modb)
ax≡1(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 2≤b≤1,0002≤b≤1,000;
对于 60%的数据, 2 ≤ b ≤ 50 , 000 , 0002 ≤ b ≤ 50 , 000 , 000 2 ≤b≤ 50,000,0002≤b≤50,000,000 2≤b≤50,000,0002≤b≤50,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 2≤a,b≤2,000,000,0002≤a,b≤2,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
x,y,m,n,L
其中 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<=2000000000,0<m、n<=2000000000,0<L<=2100000000。
输出格式
输出碰面所需要的天数,如果永远不可能碰面则输出一行"Impossible"。
输入输出样例
输入 #1
1 2 3 4 5
输出 #1
4
思路:追及问题
距
离
:
s
=
x
−
y
,
速
度
差
:
v
=
n
−
m
(
注
意
反
减
)
距离:s=x-y,速度差:v=n-m(注意反减)
距离:s=x−y,速度差:v=n−m(注意反减)
列
方
程
v
∗
x
−
L
∗
y
=
s
(
路
程
差
(
速
度
差
乘
时
间
)
减
去
若
干
圈
即
为
距
离
)
列方程v*x-L*y=s(路程差(速度差乘时间)减去若干圈即为距离)
列方程v∗x−L∗y=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;
}