[bzoj1941][SDOI2010]Hide and Seek

1941: [Sdoi2010]Hide and Seek

Time Limit: 16 Sec Memory Limit: 162 MB
Submit: 755 Solved: 425
[Submit][Status][Discuss]
Description

小猪iPig在PKU刚上完了无聊的猪性代数课,天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞,为了消除寂寞感,他决定和他的好朋友giPi(鸡皮)玩一个更加寂寞的游戏—捉迷藏。 但是,他们觉得,玩普通的捉迷藏没什么意思,还是不够寂寞,于是,他们决定玩寂寞无比的螃蟹版捉迷藏,顾名思义,就是说他们在玩游戏的时候只能沿水平或垂直方向走。一番寂寞的剪刀石头布后,他们决定iPig去捉giPi。由于他们都很熟悉PKU的地形了,所以giPi只会躲在PKU内n个隐秘地点,显然iPig也只会在那n个地点内找giPi。游戏一开始,他们选定一个地点,iPig保持不动,然后giPi用30秒的时间逃离现场(显然,giPi不会呆在原地)。然后iPig会随机地去找giPi,直到找到为止。由于iPig很懒,所以他到总是走最短的路径,而且,他选择起始点不是随便选的,他想找一个地点,使得该地点到最远的地点和最近的地点的距离差最小。iPig现在想知道这个距离差最小是多少。 由于iPig现在手上没有电脑,所以不能编程解决这个如此简单的问题,所以他马上打了个电话,要求你帮他解决这个问题。iPig告诉了你PKU的n个隐秘地点的坐标,请你编程求出iPig的问题。

Input

第一行输入一个整数N 第2~N+1行,每行两个整数X,Y,表示第i个地点的坐标

Output

一个整数,为距离差的最小值。

Sample Input

4

0 0

1 0

0 1

1 1

Sample Output

1

HINT

对于30%的数据,N<=1000 对于100%的数据,N<=500000,0<=X,Y<=10^8 保证数据没有重点保证N>=2

kd树维护平面最近,最远点对。
查询最远的时候,看一下左儿子和右儿子的四个点的坐标中分别和当前点的横纵坐标的差的绝对值最大的和。如果算出来的两个值都没有当前的答案优,那么直接返回,否则哪个答案优就先往那个儿子走。走完后看看是否还需要往另一个儿子走。
最近的估价函数就是取横纵坐标中差的绝对值最小的值加起来。如果当前的点在这个矩形区域内,那就是0。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define inf 0x7fffffff
const int N=500010;
int n,siz,root,ans,maxn,minn,D;
struct S{
    int l,r,minn[2],maxn[2],d[2];
    int &operator [] (int x){
        return d[x];
    }
    bool operator < (const S &x)const{
        return d[D]<x.d[D];
    }
}tr[N],now,p[N];
inline int IN(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
inline void update(int k){
    int l=tr[k].l,r=tr[k].r,i;
    for(i=0;i<=1;++i){
        tr[k].minn[i]=tr[k].maxn[i]=tr[k][i];
        if(l){
            tr[k].minn[i]=min(tr[k].minn[i],tr[l].minn[i]);
            tr[k].maxn[i]=max(tr[k].maxn[i],tr[l].maxn[i]);
        }
        if(r){
            tr[k].minn[i]=min(tr[k].minn[i],tr[r].minn[i]);
            tr[k].maxn[i]=max(tr[k].maxn[i],tr[r].maxn[i]);
        }
    }
}
#define mid (l+r)/2
inline int build(int l,int r,int flag){
    if(l>r) return 0;
    D=flag;nth_element(p+l,p+mid,p+r+1);
    tr[mid]=p[mid];
    tr[mid].l=build(l,mid-1,flag^1);
    tr[mid].r=build(mid+1,r,flag^1);
    update(mid);
    return mid;
}
inline int my_abs(int x){return x<0?-x:x;}
inline int calc(int x,int y,int xx,int yy){
    return my_abs(x-xx)+my_abs(y-yy);
}
inline void query_max(int k,int x,int y){
    if(!k) return ;
    int l=tr[k].l,r=tr[k].r;
    if(tr[k][0]!=x||tr[k][1]!=y) maxn=max(maxn,calc(x,y,tr[k][0],tr[k][1]));
    int ans1=max(my_abs(x-tr[l].minn[0]),my_abs(x-tr[l].maxn[0]))+max(my_abs(y-tr[l].minn[1]),my_abs(y-tr[l].maxn[1]));
    int ans2=max(my_abs(x-tr[r].minn[0]),my_abs(x-tr[r].maxn[0]))+max(my_abs(y-tr[r].minn[1]),my_abs(y-tr[r].maxn[1]));
    if(ans1>=ans2){
        if(maxn<ans1) query_max(tr[k].l,x,y);
        if(maxn<ans2) query_max(tr[k].r,x,y);
    }
    else{
        if(maxn<ans2) query_max(tr[k].r,x,y);
        if(maxn<ans1) query_max(tr[k].l,x,y);
    }
}
inline bool in(int x,int y,int x1,int y1,int x2,int y2){
    return x>=x1&&x<=x2&&y>=y1&&y<=y2;
}
inline void query_min(int k,int x,int y){
    if(!k) return ;
    int l=tr[k].l,r=tr[k].r;
    if(tr[k][0]!=x||tr[k][1]!=y) minn=min(minn,calc(x,y,tr[k][0],tr[k][1]));
    int ans1=max(0,tr[l].minn[0]-x)+max(0,x-tr[l].maxn[0])+max(0,tr[l].minn[1]-y)+max(0,y-tr[l].maxn[1]);
    int ans2=max(0,tr[r].minn[0]-x)+max(0,x-tr[r].maxn[0])+max(0,tr[r].minn[1]-y)+max(0,y-tr[r].maxn[1]);
    if(ans1<=ans2){
        if(minn>ans1) query_min(tr[k].l,x,y);
        if(minn>ans2) query_min(tr[k].r,x,y);
    }
    else{
        if(minn>ans2) query_min(tr[k].r,x,y);
        if(minn>ans1) query_min(tr[k].l,x,y);
    }
}
int main(){
    int i,x,y,j;
    n=IN();
    for(i=1;i<=n;++i){
        p[i][0]=IN(),p[i][1]=IN();
        for(j=0;j<=1;++j)
          p[i].minn[j]=p[i].maxn[j]=p[i][j];
    }
    root=build(1,n,0);
    for(ans=inf,i=1;i<=n;++i){
        maxn=0;minn=inf;
        query_max(root,p[i][0],p[i][1]);
        query_min(root,p[i][0],p[i][1]);
        ans=min(ans,maxn-minn);
    }
    printf("%d\n",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值