江西财经大学第二届程序设计竞赛同步赛----H-大时钟

首先发出题目链接:
链接: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+Ax(x=0,1,2...)
D + C ∗ y ( y = 0 , 1 , 2... ) D+C*y (y=0,1,2...) D+Cy(y=0,1,2...)

题目的意思可以转换为,当x和y为何值的时候两个等差数列的值相同,求出x和y的最小正整数解

即:
B + A ∗ x = D + C ∗ y B+A*x=D+C*y B+Ax=D+Cy
转化为:

A ∗ x − C ∗ y = D − B A*x-C*y=D-B AxCy=DB(二元一次方程组求最小正整数解)


先令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+bt
y = y 0 − a ∗ t y=y_{0}-a*t y=y0at
(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=x0c/gcd(a,b)
y = y 0 ∗ c / g c d ( a , b ) y=y_{0}*c/gcd(a,b) y=y0c/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+Ax D + C ∗ y D+C*y D+Cy相等时,当x>0,则y>0)
当B<=D时,y大于0,则x一定大于0( B + A ∗ x B+A*x B+Ax D + C ∗ y D+C*y D+Cy相等时,当y>0,则x>0)

所以当B>D时, A ∗ x + B A*x+B Ax+B为两个数列第一个相同的数;
当B<=D时, C ∗ y + D C*y+D Cy+D为两个数列第一个相同的数


举个例子

就举第一个示例,示例一化为二元一次方程为

20 ∗ x − 9 ∗ y = 17 20*x-9*y=17 20x9y=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=417/(1)=68
y = 9 ∗ 17 / ( − 1 ) = − 153 y=9*17/(-1)=-153 y=917/(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 C7+D=79+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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值