BZOJ1941: [Sdoi2010]Hide and Seek
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
0 0
1 0
0 1
1 1
Sample Output
HINT
对于30%的数据,N<=1000 对于100%的数据,N<=500000,0<=X,Y<=10^8 保证数据没有重点保证N>=2
学了一发KD-tree,搞了半天发现这就是个有思想深度的暴力
网上没有什么介绍KD-tree的详细资料,全靠yy(捂脸):
用于多维的邻值查找问题,其实也就是一棵二叉树,每次轮流按维度划分,取中间值递归
然后查找的时候就用一个估价函数发现当前这个块最优情况是否比当前情况更优,再进行访问
我至今未懂这个复杂度为啥是sqrt(n)的
还有一点就是先按估价函数更优的子树跑,但是另外一棵子树也是一定要跑的
//MT_LI #include<cmath> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; const int INF=1<<30; struct KDT{ int lc,rc,d[2],mx[2],mn[2]; }tr[1110000]; void update(int x) { int lc=tr[x].lc,rc=tr[x].rc; if(lc) { tr[x].mx[0]=max(tr[x].mx[0],tr[lc].mx[0]); tr[x].mx[1]=max(tr[x].mx[1],tr[lc].mx[1]); tr[x].mn[0]=min(tr[x].mn[0],tr[lc].mn[0]); tr[x].mn[1]=min(tr[x].mn[1],tr[lc].mn[1]); } if(rc) { tr[x].mx[0]=max(tr[x].mx[0],tr[rc].mx[0]); tr[x].mx[1]=max(tr[x].mx[1],tr[rc].mx[1]); tr[x].mn[0]=min(tr[x].mn[0],tr[rc].mn[0]); tr[x].mn[1]=min(tr[x].mn[1],tr[rc].mn[1]); } } int cmpd;//当前维护哪个维度 bool cmp(KDT a,KDT b){ return a.d[cmpd]<b.d[cmpd] ||(a.d[cmpd]==b.d[cmpd]&&a.d[cmpd^1]<b.d[cmpd^1]); } int bt(int l,int r,int d) { int mid=(l+r)/2,now; cmpd=d;now=mid; nth_element(tr+l,tr+mid,tr+r+1,cmp); tr[now].mx[0]=tr[now].mn[0]=tr[now].d[0]; tr[now].mx[1]=tr[now].mn[1]=tr[now].d[1]; if(l<mid)tr[now].lc=bt(l,mid-1,d^1); if(mid<r)tr[now].rc=bt(mid+1,r,d^1); update(now); return now; } int n,root; int nowx,nowy; int minn,maxx; int dismin(int now,int x,int y)//估价函数 { int d=0; if(x>tr[now].mx[0])d+=x-tr[now].mx[0]; if(x<tr[now].mn[0])d+=tr[now].mn[0]-x; if(y>tr[now].mx[1])d+=y-tr[now].mx[1]; if(y<tr[now].mn[1])d+=tr[now].mn[1]-y; return d; } void findmin(int now)//哪棵子树更优先遍历哪棵子树 { int dl=INF,dr=INF; int d=abs(tr[now].d[0]-nowx)+abs(tr[now].d[1]-nowy); if(d)minn=min(minn,d); if(tr[now].lc)dl=dismin(tr[now].lc,nowx,nowy); if(tr[now].rc)dr=dismin(tr[now].rc,nowx,nowy); if(dl<dr) { if(dl<minn)findmin(tr[now].lc); if(dr<minn)findmin(tr[now].rc); } else { if(dr<minn)findmin(tr[now].rc); if(dl<minn)findmin(tr[now].lc); } } int dismax(int now,int x,int y) { int d=0; d+=max(abs(tr[now].mx[0]-x),abs(tr[now].mn[0]-x)); d+=max(abs(tr[now].mx[1]-y),abs(tr[now].mn[1]-y)); return d; } void findmax(int now) { int dl=0,dr=0; int d=abs(tr[now].d[0]-nowx)+abs(tr[now].d[1]-nowy); if(d)maxx=max(maxx,d); if(tr[now].lc)dl=dismax(tr[now].lc,nowx,nowy); if(tr[now].rc)dr=dismax(tr[now].rc,nowx,nowy); if(dl>dr) { if(dl>maxx)findmax(tr[now].lc); if(dr>maxx)findmax(tr[now].rc); } else { if(dr>maxx)findmax(tr[now].rc); if(dl>maxx)findmax(tr[now].lc); } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d%d",&tr[i].d[0],&tr[i].d[1]); root=bt(1,n,0); int ans=INF; for(int i=1;i<=n;i++) { nowx=tr[i].d[0],nowy=tr[i].d[1]; minn=INF;maxx=0; findmax(root),findmin(root); ans=min(maxx-minn,ans); } printf("%d\n",ans); return 0; }