【百度之星初赛2】追星族(逆向思维,灵活题)

度度熊最近迷上了S明星,恰逢她正在巡回演出,得知所有的演出安排后,它希望一场不落的看完所有的演出。

每场演出的地点  (Xi,Yi)  以及时间 Ti ,这些信息事先都已经公布。但唯一的问题是,单位时间内度度熊的移动速度只有可怜的1。它当然希望离自己的偶像越近越好,所以它希望在所有的演出时刻,它距离演出地点距离的最大值可以最小。度度熊在时间点0时刻出发,并且它可以选择任何一个位置作为起点。

生活在一个格子化的二次元中,度度熊是这样计算距离的: |x1x2|+|y1y2|

Input

第一行一个整数T,表示T组数据。

每组样例的第一行有一个整数 N(1N50000) ,表示演出的场数。

接下来的N行,每行包括三个整数 Xi,Yi,Ti(109Xi,Yi109,0Ti109) ,描述一场演出的地点与时间。数据中存在演出时间相同或者演出地点相同的数据。

Output

对于每组数据,输出两行:

第一行输出:"Case #i:"。i代表第i组测试数据。

第二行输出最小的最大距离。为了尽量精确,用分数A/B的形式表示,其中A和B不可继续化简。

Sample Input
2
2
1 1 2
1 5 1
2
1 1 2
1 5 10
Sample Output
Case #1:
3/2
Case #2:
0/1
http://acm.hdu.edu.cn/showproblem.php?pid=5252

这题其实是应该要想到二分答案的(距离所有演出地点距离的最大值的最小值情况),然后判断此时二分的结果符不符合要求。

这时就要求转化题目解决方案。设二分结果为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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值