1. 程式人生 > >【bzoj1941】[Sdoi2010]Hide and Seek KD-tree

【bzoj1941】[Sdoi2010]Hide and Seek KD-tree

地形 容易 輸入一個整數 坐標 highlight 最小值 水平 簡單 一個點

題目描述

小豬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的問題。

輸入

第一行輸入一個整數N 第2~N+1行,每行兩個整數X,Y,表示第i個地點的坐標

輸出

一個整數,為距離差的最小值。

樣例輸入

4
0 0
1 0
0 1
1 1

樣例輸出

1


題解

KD-tree

如果我們已經知道了一個固定的點,那麽很容易求出距離它最遠和最近的點。

於是我們可以枚舉已知的點,使用KD-tree求出與一個點距離最近和最遠(好像可以貪心)的點。

其中求最遠的估價函數和最近點稍有區別,自己yy一下就好。

註意求最近點時要忽略相同的點。

#include <cstdio>
#include <algorithm>
#define N 1000010
#define inf 0x7fffffff
using namespace std;
struct data
{
    int p[2] , minn[2] , maxn[2] , c[2];
}a[N];
int d , root , ans;
bool cmp(data a , data b)
{
    return a.p[d] == b.p[d] ? a.p[d ^ 1] < b.p[d ^ 1] : a.p[d] < b.p[d];
}
void pushup(int k , int s)
{
    a[k].minn[0] = min(a[k].minn[0] , a[s].minn[0]);
    a[k].minn[1] = min(a[k].minn[1] , a[s].minn[1]);
    a[k].maxn[0] = max(a[k].maxn[0] , a[s].maxn[0]);
    a[k].maxn[1] = max(a[k].maxn[1] , a[s].maxn[1]);
}
int build(int l , int r , int now)
{
    int mid = (l + r) >> 1;
    d = now , nth_element(a + l , a + mid , a + r + 1 , cmp);
    a[mid].minn[0] = a[mid].maxn[0] = a[mid].p[0];
    a[mid].minn[1] = a[mid].maxn[1] = a[mid].p[1];
    if(l < mid) a[mid].c[0] = build(l , mid - 1 , now ^ 1) , pushup(mid , a[mid].c[0]);
    if(r > mid) a[mid].c[1] = build(mid + 1 , r , now ^ 1) , pushup(mid , a[mid].c[1]);
    return mid;
}
int getmin(int k , int x)
{
    int ret = 0;
    if(a[x].p[0] < a[k].minn[0]) ret += a[k].minn[0] - a[x].p[0];
    if(a[x].p[0] > a[k].maxn[0]) ret += a[x].p[0] - a[k].maxn[0];
    if(a[x].p[1] < a[k].minn[1]) ret += a[k].minn[1] - a[x].p[1];
    if(a[x].p[1] > a[k].maxn[1]) ret += a[x].p[1] - a[k].maxn[1];
    return ret;
}
int getmax(int k , int x)
{
    return max(abs(a[k].maxn[0] - a[x].p[0]) , abs(a[k].minn[0] - a[x].p[0])) + max(abs(a[k].maxn[1] - a[x].p[1]) , abs(a[k].minn[1] - a[x].p[1]));
}
void querymin(int k , int x)
{
	int dn = abs(a[k].p[0] - a[x].p[0]) + abs(a[k].p[1] - a[x].p[1]) , dl = inf , dr = inf;
	if(dn && dn < ans) ans = dn;
	if(a[k].c[0]) dl = getmin(a[k].c[0] , x);
	if(a[k].c[1]) dr = getmin(a[k].c[1] , x);
	if(dl < dr)
	{
		if(dl < ans) querymin(a[k].c[0] , x);
		if(dr < ans) querymin(a[k].c[1] , x);
	}
	else
	{
		if(dr < ans) querymin(a[k].c[1] , x);
		if(dl < ans) querymin(a[k].c[0] , x);
	}
}
void querymax(int k , int x)
{
	int dn = abs(a[k].p[0] - a[x].p[0]) + abs(a[k].p[1] - a[x].p[1]) , dl = 0 , dr = 0;
	if(dn > ans) ans = dn;
	if(a[k].c[0]) dl = getmax(a[k].c[0] , x);
	if(a[k].c[1]) dr = getmax(a[k].c[1] , x);
	if(dl > dr)
	{
		if(dl > ans) querymax(a[k].c[0] , x);
		if(dr > ans) querymax(a[k].c[1] , x);
	}
	else
	{
		if(dr > ans) querymax(a[k].c[1] , x);
		if(dl > ans) querymax(a[k].c[0] , x);
	}
}
int main()
{
    int n , ret = inf , tmp , i;
    scanf("%d" , &n);
    for(i = 1 ; i <= n ; i ++ ) scanf("%d%d" , &a[i].p[0] , &a[i].p[1]);
    root = build(1 , n , 0);
    for(i = 1 ; i <= n ; i ++ )
		ans = inf , querymin(root , i) , tmp = ans , ans = 0 , querymax(root , i) , ret = min(ret , ans - tmp);
    printf("%d\n" , ret);
    return 0;
}

【bzoj1941】[Sdoi2010]Hide and Seek KD-tree