877. 扩展欧几里得算法
给定 n
对正整数 ai,bi,对于每对数,求出一组 xi,yi,使其满足 ai×xi+bi×yi=gcd(ai,bi)
。
输入格式
第一行包含整数 n
。
接下来 n
行,每行包含两个整数 ai,bi
。
输出格式
输出共 n
行,对于每组 ai,bi,求出一组满足条件的 xi,yi
,每组结果占一行。
本题答案不唯一,输出任意满足条件的 xi,yi
均可。
数据范围
1≤n≤105
,
1≤ai,bi≤2×109
输入样例:
2
4 6
8 18
输出样例:
-1 1
-2 1
* a*x1+b*y1=gcd(a,b);
* b*x2+a%b*y2=gcd(b,a%b);
* because gcd(a,b)==gcd(b,a%b);
* so a*x1+b*y1=b*x2+a%b*y2;
* a*x1 + b*y1 = b*x2 + (a - floor(a/b)*b)*y2
* a*(x1 - y2) = b(x2 - floor(a/b)*y2 - y1)
* => x1=y2; y1=x2-a/b*y2;
/**
* a*x1+b*y1=gcd(a,b);
* b*x2+a%b*y2=gcd(b,a%b);
* because gcd(a,b)==gcd(b,a%b);
* so a*x1+b*y1=b*x2+a%b*y2;
* a*x1 + b*y1 = b*x2 + (a - floor(a/b)*b)*y2
* a*(x1 - y2) = b(x2 - floor(a/b)*y2 - y1)
*
* => x1=y2; y1=x2-a/b*y2;
*/
#include <iostream>
using namespace std;
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
int g=exgcd(b,a%b,x,y);
int temp=x; //存放x的值
x=y; //用得到新的x,y的值递推上一层的x,y的值,改变x的值
y=temp-a/b*y; //改变y的值
return g;
}
int main()
{
int n;
cin >> n;
while (n -- )
{
int a, b;
cin >> a >> b;
int x,y;
exgcd(a,b,x,y);
cout << x << ' ' << y << endl;
}
return 0;
}
878. 线性同余方程
给定 n
组数据 ai,bi,mi,对于每组数求出一个 xi,使其满足 ai×xi≡bi(modmi)
,如果无解则输出 impossible
。
输入格式
第一行包含整数 n
。
接下来 n
行,每行包含一组数据 ai,bi,mi
。
输出格式
输出共 n
行,每组数据输出一个整数表示一个满足条件的 xi
,如果无解则输出 impossible
。
每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。
输出答案必须在 int
范围之内。
数据范围
1≤n≤105
,
1≤ai,bi,mi≤2×109
输入样例:
2
2 3 6
4 3 5
输出样例:
impossible
-3
* a*x 与 b 模 m同余;则a*x=m*k+b;
* 即 a*x-m*k=b;
* 令y=-k,所以上式变为 a*x+m*y=b;
* 只要b%gcd(a,m)==0,则有解,否则无解。
/**
* a*x 与 b 模 m同余;则a*x=m*k+b;
* 即 a*x-m*k=b;
* 令y=-k,所以上式变为 a*x+m*y=b;
* 只要b%gcd(a,m)==0,则有解,否则无解。
*/
#include <iostream>
using namespace std;
typedef long long LL;
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
int g=exgcd(b,a%b,x,y);
int temp=x; //存放x的值
x=y; //用得到新的x,y的值递推上一层的x,y的值,改变x的值
y=temp-a/b*y; //改变y的值
return g;
}
int main()
{
int n;
cin >> n;
while (n -- )
{
int a,b,m;
cin >> a >> b >>m;
int x,y;
int d=exgcd(a,m,x,y);
if(b%d)
puts("impossible");
else
cout << (LL)x*b/d%m << endl;
}
return 0;
}
204. 表达整数的奇怪方式
给定 2n
个整数 a1,a2,…,an 和 m1,m2,…,mn,求一个最小的非负整数 x,满足 ∀i∈[1,n],x≡mi(mod ai)
。
输入格式
第 1
行包含整数 n
。
第 2…n+1
行:每 i+1 行包含两个整数 ai 和 mi
,数之间用空格隔开。
输出格式
输出最小非负整数 x
,如果 x 不存在,则输出 −1。
如果存在 x,则数据保证 x 一定在 64
位整数范围内。
数据范围
1≤ai≤231−1
,
0≤mi<ai
1≤n≤25
输入样例:
2
8 7
11 9
输出样例:
31
* n个方程,每次合并两个方程,直到还剩下一个方程即为结果。
* x 与 m1 模 a1 同余,x 与 m2 模 a2 同余;
* 所以 x=a1*k1+m1; (1)
* x=a2*k2+m2; (2)
* 即 a1*k1+m1=a2*k2+m2
* => a1*k1 - a2*k2 =m2-m1 ; (3)
* 求出满足a1*y1 - a2*y2 = gcd(a1,-a2) 的解;
* 如果(m2-m1)%gcd(a1,-a2)!=0,则无解;
* 否则有解,此时 k1=y1*(m2-m1)/gcd(a1,-a2);
* 不难而知:k1=k1+a2/gcd*k, k2=k2+a1/gcd*k;(k 为任何整数) ,(带入(3)式能证);
* 将k1 带入(1),所以x=a1*(k1+a2/gcd*k)+m1 = a1*a2/gcd*k+a1*k1+m1;
* 令 a0=a1*a2/gcd,m0=a1*k1+m1; 可得到 x=a0*k+m0,这就是(1)式与(2)式的合并结果。
/**
* n个方程,每次合并两个方程,直到还剩下一个方程即为结果。
* x 与 m1 模 a1 同余,x 与 m2 模 a2 同余;
* 所以 x=a1*k1+m1; (1)
* x=a2*k2+m2; (2)
* 即 a1*k1+m1=a2*k2+m2
* => a1*k1 - a2*k2 =m2-m1 ; (3)
* 求出满足a1*y1 - a2*y2 = gcd(a1,-a2) 的解;
* 如果(m2-m1)%gcd(a1,-a2)!=0,则无解;
* 否则有解,此时 k1=y1*(m2-m1)/gcd(a1,-a2);
* 不难而知:k1=k1+a2/gcd*k, k2=k2+a1/gcd*k;(k 为任何整数) ,(带入(3)式能证);
* 将k1 带入(1),所以x=a1*(k1+a2/gcd*k)+m1 = a1*a2/gcd*k+a1*k1+m1;
* 令 a0=a1*a2/gcd,m0=a1*k1+m1; 可得到 x=a0*k+m0,这就是(1)式与(2)式的合并结果。
*/
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
LL g=exgcd(b,a%b,x,y);
LL temp=x; //存放x的值
x=y; //用得到新的x,y的值递推上一层的x,y的值,改变x的值
y=temp-a/b*y; //改变y的值
return g;
}
int main()
{
int n;
cin >> n;
LL a1,m1;
cin >> a1 >> m1;
for(int i=1;i<n;++i)
{
LL a2,m2;
cin >> a2 >> m2;
LL k1,k2;
LL d=exgcd(a1,-a2,k1,k2);
if( (m2-m1)%d )
{
cout << -1 << endl;
return 0;
}
else
{
k1*=(m2-m1)/d; //先将其扩大 (m2-m1)/d 倍。
LL mod=a2/d;
k1=(k1%mod+mod)%mod; //得到k1的最小正整数解
m1=a1*k1+m1;
a1=a1/d*a2;
}
}
a1=abs(a1); //a1需要取绝对值,模负数可能得到不正确的结果
cout << (m1%a1+a1)%a1 << endl;
return 0;
}