首先发出题目链接:
链接:https://ac.nowcoder.com/acm/contest/635/H
来源:牛客网
涉及:gcd及扩展gcd
题目如下:
通过题目可以得到两个等差数列:
B
+
A
∗
x
(
x
=
0
,
1
,
2...
)
B+A*x (x=0,1,2...)
B+A∗x(x=0,1,2...)
D
+
C
∗
y
(
y
=
0
,
1
,
2...
)
D+C*y (y=0,1,2...)
D+C∗y(y=0,1,2...)
题目的意思可以转换为,当x和y为何值的时候两个等差数列的值相同,求出x和y的最小正整数解。
即:
B
+
A
∗
x
=
D
+
C
∗
y
B+A*x=D+C*y
B+A∗x=D+C∗y
转化为:
A ∗ x − C ∗ y = D − B A*x-C*y=D-B A∗x−C∗y=D−B(二元一次方程组求最小正整数解)
先令a=A,b=-C,c=D-B,得到二元一次方程标准形式 a x + b y = c ax+by=c ax+by=c
求解可以用到扩展欧几里得算法(扩展gcd)
扩展gcd算法是求解a’x+b’y=gcd(a’,b’)的标准算法。
ll exgcd(ll a,ll b,ll &x,ll &y){//扩展gcd
if(b==0){
x=1;
y=0;
return a;
}
ll r=exgcd(b,a%b,x,y);
ll t=x;
x=y;
y=t-a/b*y;
return r;
}
关于扩展gcd的解释可以参考其他博客,通过扩展gcd得到的x0和y0是a’x+b’y=gcd(a’,b’)的一个解,
解集:
x
=
x
0
+
b
∗
t
x=x_{0}+b*t
x=x0+b∗t
y
=
y
0
−
a
∗
t
y=y_{0}-a*t
y=y0−a∗t
(t为任意整数)
如果要求ax+by=c有整数解,即要保证c%gcd(a,b)=0(ax+by=c可以看做是a’x+b’y=gcd(a’,b’)的等式两边同乘一个数得到的)
因此一元二次方程ax+by=c解集中的最小非负整数解为
x
=
x
0
∗
c
/
g
c
d
(
a
,
b
)
x=x_{0}*c/gcd(a,b)
x=x0∗c/gcd(a,b)
y
=
y
0
∗
c
/
g
c
d
(
a
,
b
)
y=y_{0}*c/gcd(a,b)
y=y0∗c/gcd(a,b)
其中x0和y0是ax+by=gcd(a,b)通过扩展gcd得到的一个解。
下面是得到最小非负整数解的一种方法
此时将a与b同除以gcd(a,b),得到的a与b最大公约数为1,a与b是新的公差
x0=x0*c/gcd;y0=y0*c/gcd;
a=a/gcd;b=b/gcd;
由于要求x和y是非负整数,如果x0和y0小于0,可以让x0和y0同时加上(减去)公差,直到x0和y0都大于0为止,得到x和y(这只是一种方法,还有其他方法)
x=(x0%b+b)%b<0?(x0%b+b)%b+(b>0?b:-b):(x0%b+b)%b;
y=(y0%a+a)%a<0?(y0%a+a)%a+(a>0?a:-a):(y0%a+a)%a;
//x(y)对b(a)取模,得到的x(y)再加上b(a)再取模,如果x(y)仍然小于0,则还要加上abs(b)(abs(a)),最后得到的x和y都是满足条件的最小正整数
值得注意的是,x0和y0在加上(减去)公差时可能不会同时大于0
故当B>D时,x大于0,则y一定大于0(
B
+
A
∗
x
B+A*x
B+A∗x与
D
+
C
∗
y
D+C*y
D+C∗y相等时,当x>0,则y>0)
当B<=D时,y大于0,则x一定大于0(
B
+
A
∗
x
B+A*x
B+A∗x与
D
+
C
∗
y
D+C*y
D+C∗y相等时,当y>0,则x>0)
所以当B>D时,
A
∗
x
+
B
A*x+B
A∗x+B为两个数列第一个相同的数;
当B<=D时,
C
∗
y
+
D
C*y+D
C∗y+D为两个数列第一个相同的数
举个例子
就举第一个示例,示例一化为二元一次方程为
20
∗
x
−
9
∗
y
=
17
20*x-9*y=17
20∗x−9∗y=17
(A=20,B=2,C=9,D=19)
(a=A=20,b=-B=-9,c=D-B=17)
调用扩展gcd函数并返回gcd(20,-9)
e x g c d ( 20 , − 9 , x , y ) exgcd(20,-9,x,y) exgcd(20,−9,x,y)
返回gcd=-1(由于公差A和C都大于零,所以返回的gcd(a,-c)肯定小于0)
得到x=4,y=9
故原二元一次方程的一个解为
x
=
4
∗
17
/
(
−
1
)
=
−
68
x=4*17/(-1)=-68
x=4∗17/(−1)=−68
y
=
9
∗
17
/
(
−
1
)
=
−
153
y=9*17/(-1)=-153
y=9∗17/(−1)=−153
新的公差
a
=
a
/
g
c
d
(
a
,
b
)
=
−
20
a=a/gcd(a,b)=-20
a=a/gcd(a,b)=−20
b
=
b
/
g
c
d
(
a
,
b
)
=
9
b=b/gcd(a,b)=9
b=b/gcd(a,b)=9
由于得到的解x和y都小于0,需要找到最小非负整数解
且示例1中D>B(19>2),所以应当考虑当y>0情况
y的最小非负整数解为
y=((y%a)+a)%a=-13
y<0,则y需要再加上一个abs(a)
y=y+abs(a)=7;
得到y的最小非负整数解y=7(此时x一定大于0)
故数列第一次相同的数为
C
∗
7
+
D
=
7
∗
9
+
19
=
82
C*7+D=7*9+19=82
C∗7+D=7∗9+19=82
代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y){//扩展gcd
if(b==0){
x=1;
y=0;
return a;
}
ll r=exgcd(b,a%b,x,y);
ll t=x;
x=y;
y=t-a/b*y;
return r;
}
int main(){
ll A,B,C,D,x,y;
cin>>A>>B>>C>>D;
ll a=A,b=-C,c=D-B;//代换得到二元一次方程标准式子
ll gcd=exgcd(a,b,x,y);//得到最小公因数
if(c%gcd!=0) return !(cout<<"-1");//不存在非负整数解条件
//以下是算出最小非负整数解得方法
x=x*c/gcd;y=y*c/gcd;//得到二元一次不等式的解
a=a/gcd;b=b/gcd;//得到最简公差
if(B>D) x=(x%b+b)%b<0?(x%b+b)%b+abs(b):(x%b+b)%b,cout<<A*x+B;//以x与y的最小非负整数解中x与y最小的那个值为标准输出答案(理由在上面)
else y=(y%a+a)%a<0?(y%a+a)%a+abs(a):(y%a+a)%a,cout<<C*y+D;
return 0;
}