[ABC193E] Oversleeping
作者:互联网
前言
扩展欧几里得?几万年前学的东西,幸好最后写出来了,但还是值得复习一下(指水题解)。
题目
题目大意:
\(T\) 组数据。
时间从 \(0\) 时刻开始,有一辆火车从 \(A\) 地出发,在 \(A\) 地与 \(B\) 地之间来回跑,单趟时间为 \(X\) 秒,每到一个地点停 \(Y\) 秒。
你初始在 \(B\) 地,先睡 \(P\) 秒,再醒着 \(Q\) 秒,一直循环。
询问你是否可以上车,即是否存在一个时刻你醒着,且车停在 \(B\) 地。如果可以,输出最小的上车时间。如果不能,输出 infinity
。
\(1\le T\le 10;1\le X,P\le 10^9;1\le Y,Q\le 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,j\ge 0\),但此时我们需要解的方程其实是:\(aj-bi=kd\),但扩欧解的方程是 \(aj+bi=d\),所以我们解出来的 \(i\) 需要满足小于等于 \(0\)。
找最小的时间其实就是找最大的 \(i\in(-\infty,0]\) 且最小的 \(j\in[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\n");
else Put(ans,'\n');
}
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
*/
标签:le,ABC193E,LL,t2,Oversleeping,t1,ans,woc 来源: https://www.cnblogs.com/PPLPPL/p/14806488.html