一、同余
1、一些普通的性质
1.反身性:
a
≡
a
(
m
o
d
m
)
a≡a (mod m)
a≡a(modm);
2.对称性:若
a
≡
b
(
m
o
d
m
)
a≡b(mod m)
a≡b(modm),则
b
≡
a
(
m
o
d
m
)
b≡a (mod m)
b≡a(modm);
3.传递性:若
a
≡
b
(
m
o
d
m
)
a≡b(mod m)
a≡b(modm),
b
≡
c
(
m
o
d
m
)
b≡c(mod m)
b≡c(modm),则
a
≡
c
(
m
o
d
m
)
a≡c(mod m)
a≡c(modm);
4.同余式相加(减):若
a
≡
b
(
m
o
d
m
)
a≡b(mod m)
a≡b(modm),
c
≡
d
(
m
o
d
m
)
c≡d(mod m)
c≡d(modm),则
a
+
c
≡
b
+
d
(
m
o
d
m
)
a+c≡b+d(mod m)
a+c≡b+d(modm);
5.同余式相乘:若
a
≡
b
(
m
o
d
m
)
,
c
≡
d
(
m
o
d
m
)
a≡b(mod m),c≡d(mod m)
a≡b(modm),c≡d(modm),则
a
c
≡
b
d
(
m
o
d
m
)
ac≡bd(mod m)
ac≡bd(modm)。
2、一些同余式性质
1、同余式一边的数可以移到另一边,只要改变符号就可以了。
a + c ≡ b ( m o d m ) = > a ≡ b − c ( m o d m ) a+c≡b (mod m) => a≡b-c(mod m) a+c≡b(modm)=>a≡b−c(modm)
2、同余式的每一边都可以增加或减去模的任意倍数。
a ≡ b ( m o d m ) = > a + k m ≡ b ( m o d m ) = > a − k m ≡ b ( m o d m ) a≡b(mod m) => a+km≡b(mod m) => a-km≡b(mod m) a≡b(modm)=>a+km≡b(modm)=>a−km≡b(modm)
3、同余式两边的数和模可以同时乘上一个整数。
a ≡ b ( m o d m ) = > k a ≡ k b ( m o d k m ) a≡b(mod m) => ka≡kb(mod km) a≡b(modm)=>ka≡kb(modkm)
二、欧几里得算法(最大公约数gcd)
1、一些性质(证明过程简单了解就好)
g c d ( a , b ) = g c d ( b , a ) gcd(a,b)=gcd(b,a) gcd(a,b)=gcd(b,a)
g c d ( a , b ) = g c d ( a − b , b ) ( a ≥ b ) gcd(a,b)=gcd(a-b,b)(a\ge b) gcd(a,b)=gcd(a−b,b)(a≥b)
g c d ( a , b ) = g c d ( a % b , b ) gcd(a,b)=gcd(a\%b,b) gcd(a,b)=gcd(a%b,b)
g c d ( a , b , c ) = g c d ( g c d ( a , b ) , c ) gcd(a,b,c)=gcd(gcd(a,b),c) gcd(a,b,c)=gcd(gcd(a,b),c)
g c d ( k a , k b ) = k g c d ( a , b ) gcd(ka,kb)=k\ gcd(a,b) gcd(ka,kb)=k gcd(a,b)
2、求最大公约数模板
typedef long long ll;
ll gcd(ll a, ll b){
return b?gcd(b,a%b):a;
}
注意当
b
≠
0
b\neq 0
b=0 时,返回值为 gcd(b,a%b)
而不是 gcd(a%b,b)
,否则会不断递归导致栈溢出(因为这样子的话b永远是b)
三、扩展欧几里得算法(重点)
一、看这个二元一次不定方程
a
x
+
b
y
=
c
ax+by=c
ax+by=c
其中
a
,
b
,
c
a,b,c
a,b,c 是已知的正整数,如何求出方程的解呢?
- 观察: 显然这可以看做一次函数,有无穷个实数解。不过我们研究的是整数解,这个方程有没有整数解就不一定了。
上述方程有解的充要条件是 g c d ( a , b ) ∣ c gcd(a,b)|c gcd(a,b)∣c ( c c c 是 g c d ( a , b ) gcd(a,b) gcd(a,b) 的倍数)
可以理解为, g c d ( a , b ) gcd(a,b) gcd(a,b) 是 a x + b y ax+by ax+by 可以表示出的最小正整数。
贝祖定理(裴蜀定理):任意两个整数a,b,最大公约数为d=gcd(a,b),那么对于任意的整数x,y,ax+by=m,构成的m一定是d的整数倍(即m%d=0)
方程
a
x
+
b
y
=
d
,
d
=
g
c
d
(
a
,
b
)
ax+by=d,d=gcd(a,b)
ax+by=d,d=gcd(a,b) 的所有解为
{
x
=
x
0
+
b
d
t
y
=
y
0
−
a
d
t
\begin{cases} x=x_0+\frac{b}{d}t\\ y=y_0-\frac{a}{d}t \end{cases}
{x=x0+dbty=y0−dat其中
x
0
,
y
0
x_0,y_0
x0,y0 是一组特解,可以理解为任意解,只要有一组
x
0
,
y
0
x_0,y_0
x0,y0,我们就能求出所有整数解(正负都可以)。
二、方程
a
x
+
b
y
=
c
①
,
g
c
d
(
a
,
b
)
∣
c
ax+by=c①,gcd(a,b)|c
ax+by=c①,gcd(a,b)∣c 的所有解为
{
x
=
c
d
x
0
+
b
d
t
y
=
c
d
y
0
−
a
d
t
\begin{cases} x=\frac{c}{d}x_0+\frac{b}{d}t\\ y=\frac{c}{d}y_0-\frac{a}{d}t \end{cases}
{x=dcx0+dbty=dcy0−dat其中
x
0
,
y
0
x_0,y_0
x0,y0 是方程
a
x
+
b
y
=
d
,
d
=
g
c
d
(
a
,
b
)
ax+by=d,d=gcd(a,b)
ax+by=d,d=gcd(a,b) 的一组特解(注意,不是
a
x
+
b
y
=
c
ax+by=c
ax+by=c)
看不懂的话可以这样解释:
说白了就是我们通过计算
a
x
′
+
b
y
′
=
g
c
d
(
a
,
b
)
②
ax'+by'=gcd(a,b)②
ax′+by′=gcd(a,b)②的解 (
x
′
,
y
′
x',y'
x′,y′因为和方程①的不一样,所以区别开来)然后方程两边同乘
c
g
c
d
(
a
,
b
)
\frac{c}{gcd(a,b)}
gcd(a,b)c,得到
a
x
′
c
g
c
d
(
a
,
b
)
+
b
y
′
c
g
c
d
(
a
,
b
)
=
c
a\frac{x'c}{gcd(a,b)}+b\frac{y'c}{gcd(a,b)}=c
agcd(a,b)x′c+bgcd(a,b)y′c=c,按照方程①,我们把那一坨换成
x
,
y
x,y
x,y即可。
{
x
=
c
g
c
d
(
a
,
b
)
x
′
y
=
c
g
c
d
(
a
,
b
)
y
′
\begin{cases} x=\frac{c}{gcd(a,b)}x'\\ y=\frac{c}{gcd(a,b)}y' \end{cases}
{x=gcd(a,b)cx′y=gcd(a,b)cy′因此我们就能由
a
x
′
+
b
y
′
=
g
c
d
(
a
,
b
)
②
ax'+by'=gcd(a,b)②
ax′+by′=gcd(a,b)②的解求出
a
x
+
b
y
=
c
①
ax+by=c①
ax+by=c①的解了。
扩展欧几里得算法模板:
事实上和欧几里得算法框架相同,不过递归过程中我们可以计算出 x , y x,y x,y。
ll exgcd(ll a, ll b,ll &x,ll &y){
if(!b){
x=1;y=0;return a;
}
ll res=exgcd(b,a%b);
ll tmp=x;
x=y;
y=tmp-(a/b)*y;
return res;
}
或者
ll extgcd(ll a,ll b,ll& x,ll& y){
ll d = a;
if (!b){
x = 1;y = 0;
}else{
d = extgcd(b,a%b,y,x);//和欧几里得一样有递归
y -= a/b*x;
}
return d;
}
洛谷 P1516 青蛙的约会
(广东工业大学ACM寒假集训 专题四 G)
专题学习链接https://vjudge.net/contest/479577#problem/G
题目描述
两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。
我们把这两只青蛙分别叫做青蛙 A 和青蛙 B,并且规定纬度线上东经 00 度处为原点,由东往西为正方向,单位长度 11 米,这样我们就得到了一条首尾相接的数轴。设青蛙 A 的出发点坐标是 xx,青蛙 B 的出发点坐标是 yy。青蛙 A 一次能跳 mm 米,青蛙 B 一次能跳 nn 米,两只青蛙跳一次所花费的时间相同。纬度线总长 LL 米。现在要你求出它们跳了几次以后才会碰面。
输入格式
输入只包括一行五个整数
x
,
y
,
m
,
n
,
L
x,y,m,n,L
x,y,m,n,L。
输出格式
输出碰面所需要的天数,如果永远不可能碰面则输出一行一个字符串 Impossible
。
输入输出样例
说明/提示
100
%
100\%
100% 的数据,
1
≤
x
≠
y
≤
2
×
1
0
9
1 \le x \ne y \le 2 \times 10^{9}
1≤x=y≤2×109,
1
≤
m
,
n
≤
2
×
1
0
9
1 \le m, n \le 2 \times 10^{9}
1≤m,n≤2×109,
1
≤
L
≤
2.1
×
1
0
9
1 \le L \le 2.1 \times 10^{9}
1≤L≤2.1×109
简要分析:
如果青蛙跳的长度为无限长,那么我们相当于要解
x
+
k
m
=
y
+
k
n
x+km=y+kn
x+km=y+kn这个方程。
如果算上了
L
L
L。
就是
x
+
k
m
≡
y
+
k
n
(
m
o
d
L
)
x+km≡y+kn(mod L)
x+km≡y+kn(modL)
移一下 k ( m − n ) ≡ y − x ( m o d L ) k(m-n)≡y-x(mod L) k(m−n)≡y−x(modL)
k k k未知, ( m − n ) (m-n) (m−n)已知, ( y − x ) (y-x) (y−x)已知。
可以化为 k ( m − n ) + t L = y − x k(m-n)+t L=y-x k(m−n)+tL=y−x
令 a = m − n ; c = y − x ; b = L a=m-n;c=y-x;b=L a=m−n;c=y−x;b=L
就变成了我们熟悉的 a x + b y = c ① ax+by=c① ax+by=c①
如果 a = m − n < 0 a=m-n<0 a=m−n<0,我们就要让 a = − a , c = − c a=-a,c=-c a=−a,c=−c
#include<iostream>
typedef long long ll;
using namespace std;
ll x,y;
ll exgcd(ll a, ll b){
if(!b){
x=1;
y=0;
return a;
}
ll res=exgcd(b,a%b);
ll tmp=x;
x=y;
y=tmp-(a/b)*y;
return res;
}
int main()
{
ll xx,yy,n,m,l;
cin>>xx>>yy>>m>>n>>l;
ll a=m-n<0?n-m:m-n;
ll c=m-n<0?xx-yy:yy-xx;
ll b=l;
ll gcd=exgcd(a,b);
//cout<<a<<' '<<b<<' '<<c<<' '<<x<<' '<<gcd<<' ';
if(c%gcd!=0)cout<<"Impossible";
else {
//x=x*(c/gcd);
cout<<(x*(c/gcd)%(b/gcd)+b/gcd)%(b/gcd);
}
}