这题其实是应该要想到二分答案的(距离所有演出地点距离的最大值的最小值情况),然后判断此时二分的结果符不符合要求。
这时就要求转化题目解决方案。设二分结果为x。
总共有n场位于不同地点的演出。以时间排序,那么,相邻两场演唱会之间的距离是可求的。然而我们并不要求一定要到演唱会的点,只是求距演唱会的最大值的最小值情况。画出可以在x秒内到达第一场演唱会的区域,这些位置都是他初始可在的位置。然后与它相邻的是可在x秒到第二场演唱会的扩展区域。由于中间还有t2-t1秒时间可以随便移动,那么不妨把第二个扩展区域再按这个时间扩展出去,形成新的第二扩展区域。将它与第一扩展区域相交,产生的部分就是符合条件的度度熊可以在第一场演唱会开始时待的位置,记为w。同样地对第三扩展区域进行类似操作,再与w相交,形成新的w'。以此类推,判断交完最后一个扩展区域后还有没有公共部分(相切也算有)。
注意到答案有出现分数的情况。但细想一下就会发现只有ans/1和ans/2(因为点的坐标都是整数的,本着不浪费原则,几次的相交区域里必然会有至少一次刚好相切,在整数情况下,可能无法避免某块相交区域的长或宽是1,这个时候,二分的答案-0.5的话就相当于产生相交区域的两块矩形扩展区域分别-0.5,加起来就是1了,而分数是其它的情况则不可能实现这个),所以可以在输入坐标时*2。这是其中一个注意点。
另外一个需要稍加处理的是求相交区域的时候。
我们知道,直线的方程式是y=ax+b。这道题里直线的斜率是已知的(1/-1),一个相交区域有四条边,对应四条直线。可以求出每条直线的b,再分别两边缩小范围。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
struct show
{
ll x,y,t;
bool operator < (const show &q) const
{
return t<q.t;
}
}s[50005];
int main()
{
int T;
scanf("%d",&T);
for(int ca=1;ca<=T;ca++)
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%I64d%I64d%I64d",&s[i].x,&s[i].y,&s[i].t);
s[i].x*=2;
s[i].y*=2;
}
sort(s,s+n);
ll l=0,r=8000000000LL;
while(l<r)
{
ll m=(l+r)>>1;
ll u_u=s[0].x+s[0].y+m;
ll u_d=s[0].x-s[0].y+m;
ll d_u=s[0].x+s[0].y-m;
ll d_d=s[0].x-s[0].y-m;
bool isok=1;
for(int i=1;i<n;i++)
{
ll t=s[i].t-s[i-1].t;
u_u=min(u_u+2*t,s[i].x+s[i].y+m);
u_d=min(u_d+2*t,s[i].x-s[i].y+m);
d_u=max(d_u-2*t,s[i].x+s[i].y-m);
d_d=max(d_d-2*t,s[i].x-s[i].y-m);
if(u_u<d_u || u_d<d_d)isok=0;
}
if(isok)r=m;
else l=m+1;
}
printf("Case #%d:\n",ca);
if(l%2==0)printf("%I64d/1\n",l/2);
else printf("%I64d/2\n",l);
}
return 0;
}