扩展欧几里得
简单证明
1299. 五指山
五指山
思路
1.扩展欧几里得 acwing 877
在求最大公约数同时求得一组 x,y
满足gcd(a, b) = a * x + b * y
gcd
(
a
,
b
)
=
g
c
d
(
b
,
a
%
b
)
\gcd(a,\ b) = gcd(b,\ a\ \%\ b)
gcd(a, b)=gcd(b, a % b)
a
x
+
b
y
=
b
∗
x
0
+
(
a
%
b
)
∗
y
0
ax+by = b*x0 + (a \% b)\ *y0
ax+by=b∗x0+(a%b) ∗y0
由于
a
%
b
=
a
−
⌊
a
b
⌋
∗
b
a\%b = a - \lfloor\frac{a}{b}\rfloor\ *b
a%b=a−⌊ba⌋ ∗b
整理得
a
x
+
b
y
=
a
∗
y
0
+
b
∗
(
x
0
−
⌊
a
b
⌋
∗
y
0
)
ax+by = a*y0 + b\ *(x0-\lfloor\frac{a}{b}\rfloor\ *y0)
ax+by=a∗y0+b ∗(x0−⌊ba⌋ ∗y0)
即递归根据下一层得x,y求得当前层得x,y
x
=
y
0
x=y0
x=y0
y
=
x
0
−
⌊
a
b
⌋
∗
y
0
y=x0-\lfloor\frac{a}{b}\rfloor\ *y0
y=x0−⌊ba⌋ ∗y0
2.
a
∗
x
0
+
b
∗
y
0
=
g
c
d
(
a
,
b
)
=
d
a\ *x0+b\ *y0=gcd(a,b)=d
a ∗x0+b ∗y0=gcd(a,b)=d
扩展欧几里得求得x0,y0
x,y满足
x
=
x
0
±
k
∗
b
d
x=x0±k\ *\frac{b}{d}
x=x0±k ∗db,
y
=
y
0
∓
k
∗
a
d
y=y0∓k\ *\frac{a}{d}
y=y0∓k ∗da
3.
本题可抽象出
x
+
a
d
≡
y
(
m
o
d
n
)
x+ad≡y(mod\ n)
x+ad≡y(mod n)
x
+
a
d
=
y
+
b
n
x+ad=y+bn
x+ad=y+bn 此处的a和b为变量
整理得
a
d
−
b
n
=
y
−
x
ad-bn=y-x
ad−bn=y−x
由扩展欧几里得
a
d
−
b
n
=
g
c
d
(
d
,
n
)
ad-bn=gcd(d,\ n)
ad−bn=gcd(d, n) 要使得a有解需要满足
y
−
x
y-x
y−x是
g
c
d
(
d
,
n
)
gcd(d, n)
gcd(d,n)的倍数
题目求a的最小值,由
2.
得
a
=
a
0
+
k
∗
n
g
c
d
(
d
,
n
)
a=a0+k\ *\frac{n}{gcd(d,\ n)}
a=a0+k ∗gcd(d, n)n
a
0
=
a
m
o
d
n
g
c
d
(
d
,
n
)
a0=a\ mod\ \frac{n}{gcd(d,\ n)}
a0=a mod gcd(d, n)n 为所求值
样例输入:
2
3 2 0 2
3 2 0 1
样例输出:
1
2
代码:
#include<iostream>
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
LL e, x0, y0;
e = exgcd(b, a % b, x0, y0);
x = y0, y = x0 - a / b * y0;
return e;
}
int main()
{
int T;
cin >> T;
while(T -- )
{
LL n, d, a, b, x, y;
cin >> n >> d >> x >> y;
LL e = exgcd(d, n, a, b);
if((y - x) % e) cout << "Impossible" << endl;
else
{
a = (a * ((y - x) / e));
n /= e;
cout << (a % n + n) % n<< endl; //处理负数
}
}
return 0;
}
1301. C 循环
C 循环
样例输入:
3 3 2 16
3 7 2 16
7 3 2 16
3 4 2 16
0 0 0 0
样例输出:
0
2
32766
FOREVER
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y) //拓展欧几里得算法 ax + by = d
{
if (b == 0) //边界情况
{
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x); //y*b + x(a mod b) = d;
y -= a / b * x; //公式推系数(下方图一)
return d;
}
int main()
{
LL a, b, c, k;
while (cin >> a >> b >> c >> k, a || b || c || k) //逗号表达式
{
LL x, y; //系数 公式:x*C - y2^k = B - A
LL z = 1ll << k; //为了方便,公式:x*C - y*z = B - A
LL d = exgcd(c, z, x, y);
if ((b - a) % d) //有没有解取决于B - A是否能整除d
cout << "FOREVER" << endl; //求余不为0,一定无解
else //否则一定有解
{
x *= (b - a) / d; //因为等式右边原本是d,现在是(B - A), 所以等式右边x, y乘上(B-A)/d
z /= d; //根据公式,x = x0 + k*(b/d),这里的b就是z,此时所有的解x和x0的差,都是z的倍数
cout << (x % z + z) % z << endl; //x的最小非负整数解,就是x0 mod z, c++中取模可能是负数
}
}
return 0;
}