前言
扩展欧几里得?几万年前学的东西,幸好最后写出来了,但还是值得复习一下(指水题解)。
题目
题目大意:
(T) 组数据。
时间从 (0) 时刻开始,有一辆火车从 (A) 地出发,在 (A) 地与 (B) 地之间来回跑,单趟时间为 (X) 秒,每到一个地点停 (Y) 秒。
你初始在 (B) 地,先睡 (P) 秒,再醒着 (Q) 秒,一直循环。
询问你是否可以上车,即是否存在一个时刻你醒着,且车停在 (B) 地。如果可以,输出最小的上车时间。如果不能,输出 infinity
。
(1le Tle 10;1le X,Ple 10^9;1le Y,Qle 500.)
讲解
你看这个 (Y,Q) 这么小,一看就可以枚举,记为 (y,q)。
那么这就可以转换为一个同余方程问题,我们设火车经历 (i) 个周期,人经历 (j) 个周期,可列出方程:
[X+i(2X+2Y)+y = Pj+Q(j-1)+q
]
变形可得:
[j(P+Q)-i(2X+2Y) = Q+X+y-q
]
令 (a=P+Q,b=(2X+2Y),d=gcd(a,b)),于是用扩展欧几里得先求出特殊解,然后再找最小的时间即可。
值得注意的是,(i,jge 0),但此时我们需要解的方程其实是:(aj-bi=kd),但扩欧解的方程是 (aj+bi=d),所以我们解出来的 (i) 需要满足小于等于 (0)。
找最小的时间其实就是找最大的 (iin(-infty,0]) 且最小的 (jin[0,+infty)),再根据他们算就行。
实现其实不难。
代码
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b)
{
x = 1;
y = 0;
return a;
}
LL ret = exgcd(b,a%b,y,x);
y -= x*(a/b);
return ret;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
for(int T_T = Read(); T_T ;-- T_T)
{
X = Read(); Y = Read(); //on the way,stopping
P = Read(); Q = Read(); //asleep,awake
LL ans = INF,J,I;
LL d = exgcd(P+Q,2*(X+Y),J,I);
for(int y = 0;y < Y;++ y)
for(int q = 0;q < Q;++ q)
{
LL deng = Q+X+y-q,i,j;
if(deng % d) continue;
j = J; i = I;
LL t1 = (P+Q) / d,t2 = 2*(X+Y) / d; i *= (deng / d); j *= (deng / d);
if(i > 0)
{
LL woc = i / t1;
i -= woc * t1;
j += woc * t2;
}
if(j < 0)
{
LL woc = (-j) / t2;
j += woc * t2;
i -= woc * t1;
}
while(i > 0 || j < 0) i = i - t1,j = j + t2;
LL wrng = Min((-i) / t1,j / t2);//both too large
i += wrng * t1;
ans = Min(ans,X+y+2*(-i)*(X+Y));
}
if(ans == INF) printf("infinity
");
else Put(ans,'
');
}
return 0;
}
/*
草稿纸
X+2iX+2iY+y = Pj+Q(j-1)+q
X+2iX+2iY+y = Pj+Qj-Q+q
2i(X+Y)-j(P+Q) = q-Q-X-y
j(P+Q)-i(2X+2Y) = Q+X+y-q
a(x+b)+b(y-a) = d
k(a(x+b)+b(y-a)) = kd
*/