扩展欧几里德和贝祖定理
概述
扩展欧几里德用来求解有关方程\(ax+by=m\) 的问题
扩欧模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14 int exgcd ( int a , int b , int & x , int & y )
{
if ( b == 0 )
{
x = 1 ;
y = 0 ;
return a ;
}
int ans = exgcd ( b , a % b , x , y );
int tmp = x ;
x = y ;
y = tmp - ( a / b ) * y ;
return ans ;
}
判断方程\(ax+by=m\) 或\(ax≡m(mod\ b)\) 是否有解
思路: 当\(gcd(a,b)|m\) 时 方程有解,反之则无解
求解方程\(ax+by=m( ax≡m(mod\ b) )\) 的最小正整数解
思路
1.首先判断方程是否有解,无解则直接退出
2.扩欧求\(x\) ,令\(bg=m/gcd(a,b),则x=(x\%bg+bg)\%bg\)
例1.洛谷 P1082 同余方程
题目描述
求关于x的同余方程ax≡1(mod b)的最小正整数解。
输入格式
一行,包含两个正整数a,b,用一个空格隔开。
输出格式
一个正整数x0,即最小正整数解。输入数据保证一定有解。
输入样例
输出样例
题解
模板题,由于题目规定一定有解,因此\(a⊥b(a与b互质)\) ,此时既可以用扩欧求解,也可用欧拉定理求解
在这科普一下欧拉定理:
\(a^φ(n) ≡1(mod\ n)(其中,a与n均为正整数,且两者互质。)\) 费马小定理为欧拉定理的特殊形式
代码
扩欧版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 #include <bits/stdc++.h>
using namespace std ;
#define int long long
int exgcd ( int a , int b , int & x , int & y )
{
if ( b == 0 )
{
x = 1 ;
y = 0 ;
return a ;
}
int ans = exgcd ( b , a % b , x , y );
int tmp = x ;
x = y ;
y = tmp - ( a / b ) * y ;
return ans ;
}
signed main ()
{
int a , b ;
int x , y ;
cin >> a >> b ;
exgcd ( a , b , x , y );
x = ( x % b + b ) % b ;
cout << x << endl ;
return 0 ;
}
欧拉定理版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 #include <bits/stdc++.h>
using namespace std ;
#define int long long
int qpow ( int a , int n , int b )
{
int ans = 1 ;
while ( n )
{
if ( n & 1 ) ans = ans % b * a % b ;
a = a % b * a % b ;
n >>= 1 ;
}
return ans ;
}
int euler ( int n )
{
int ans = n ;
for ( int i = 2 ; i * i <= n ; i ++ )
{
if ( n % i == 0 ) ans -= ans / i ;
while ( n % i == 0 ) n /= i ;
}
if ( n > 1 ) ans -= ans / n ;
return ans ;
}
signed main ()
{
int a , b ;
cin >> a >> b ;
cout << qpow ( a , euler ( b ) -1 , b ) << endl ;
return 0 ;
}
例2.洛谷P5656 二元一次方程
题目描述
给定不定方程ax+by=c
若该方程无整数解,输出-1。
若该方程有整数解,且有正整数解,则输出其正整数解的数量,所有正整数解中x的最小值,所有正整数解中y的最小值,所有正整数解中x的最大值,以及所有正整数解中y 的最大值。
若方程有整数解,但没有正整数解,你需要输出所有整数解中x的最小正整数值,y的最小正整数值。
正整数解即为 x, y 均为正整数的解, 0 不是正整数。
整数解即为 x,y 均为整数的解。
x的最小正整数值即所有x为正整数的整数解中x的最小值,y同理。
输入格式
第一行一个正整数 T,代表数据组数。
接下来 T 行,每行三个由空格隔开的正整数a,b,c。
输出格式
T 行。
若该行对应的询问无整数解,一个数字 -1。
若该行对应的询问有整数解但无正整数解,包含 2个由空格隔开的数字,依次代表整数解中,x的最小正整数值,y 的最小正整数值。
否则包含5个由空格隔开的数字,依次代表正整数解的数量,正整数解中,x的最小值,y的最小值,x的最大值,y的最大值。
输入样例
7
2 11 100
3 18 6
192 608 17
19 2 60817
11 45 14
19 19 810
98 76 5432
输出样例
4 6 2 39 8
2 1
-1
1600 1 18 3199 30399
34 3
-1
2 12 7 50 56
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 #include <bits/stdc++.h>
using namespace std ;
#define int long long
int exgcd ( int a , int b , int & x , int & y )
{
if ( b == 0 )
{
x = 1 ;
y = 0 ;
return a ;
}
int ans = exgcd ( b , a % b , x , y );
int tmp = x ;
x = y ;
y = tmp - ( a / b ) * y ;
return ans ;
}
signed main ()
{
int t , a , b , c ;
int x , y ;
int minx , miny ;
int maxx , maxy ;
int cnt = 1 ;
scanf ( "%lld" , & t );
bool flag = false ;
while ( t -- )
{
flag = false ;
cnt = 0 ;
scanf ( "%lld %lld %lld" , & a , & b , & c );
int gcd = exgcd ( a , b , x , y );
if ( c % gcd )
{
cout << "-1" << endl ;
continue ;
}
int bg = b / gcd ;
int cg = c / gcd ;
int ag = a / gcd ;
int tmp ;
x *= cg ;
y *= cg ;
//cout<<"x= "<<x<<"y="<<y<<endl;
tmp = x ;
x = ( x % bg + bg -1 ) % bg + 1 ; //最小正整数解 若是最小整数解则不用-1
int ansy = ( y % ag + ag -1 ) % ag + 1 ;
int t = ( x - tmp ) / bg ;
y -= t * ag ;
if ( x > 0 && y > 0 )
{
minx = x ;
maxy = y ;
flag = true ;
cnt = 1 ;
int k = y / ag ;
if ( y % ag == 0 )
{
cnt += k -1 ;
miny = ag ;
maxx = x + ( k -1 ) * bg ;
}
else
{
cnt += k ;
miny = y - k * ag ;
maxx = x + k * bg ;
}
}
if ( flag ) printf ( "%lld %lld %lld %lld %lld \n " , cnt , minx , miny , maxx , maxy );
else printf ( "%lld %lld \n " , x , ansy );
}
return 0 ;
}