HYSBZ bzoj 1941 Hide and Seek

来源:互联网 发布:java项目开发源代码 编辑:程序博客网 时间:2024/06/16 03:07

Problem

www.lydsy.com/JudgeOnline/problem.php?id=1941
vjudge.net/contest/187908#problem/B

Reference

[BZOJ1941][Sdoi2010]Hide and Seek(kd-tree)

Meaning

给出平面上 n 个点,要选其中一个,使得它到其它点的 曼哈顿最远距离和最近距离的差 最小,求这个最小的差。

Analysis

用 K-D树对每个点都求离它最远的点和最近的点的距离,相减更新答案。

Notes

这题集合了 K-D树求最远点和最近点的套路,主要在于那两个估价减枝。
在建树的时候,处理出每个点划分的那个区域里的横、纵坐标的最大、最小值,相当于找到包住这个区域的那个大矩形。
在最远点估价的时候,相当于用这个矩形的四个顶点之一(离目标点最远那个)来估价;在最近点时,相当于判断目标点在不在这个矩形内,如果在,直接不算距离,(某一维)在矩形外才算上目标点(在该维度)离最近的矩形边界的距离。

Code

#include <cmath>#include <algorithm>#include <iostream>using namespace std;const int N = 500000, D = 2, X = 1e8, Y = X;int idx;struct node{    int v[D];    bool operator < (const node &rhs) const    {        return v[idx] < rhs.v[idx];    }} kdt[N], aim;int lc[N], rc[N], big[N][D], sml[N][D];void pushup(int p){    for(int i = 0; i < D; ++i)    {        int &b = big[p][i], &s = sml[p][i];        if(~lc[p])        {            b = max(b, big[lc[p]][i]);            s = min(s, sml[lc[p]][i]);        }        if(~rc[p])        {            b = max(b, big[rc[p]][i]);            s = min(s, sml[rc[p]][i]);        }    }}int build(int l, int r, int d){    idx = d & 1;    int m = l + r >> 1;    nth_element(kdt + l, kdt + m, kdt + r + 1);    lc[m] = rc[m] = -1;    for(int i = 0; i < D; ++i)        big[m][i] = sml[m][i] = kdt[m].v[i];    if(l < m)        lc[m] = build(l, m - 1, d + 1);    if(r > m)        rc[m] = build(m + 1, r, d + 1);    pushup(m);    return m;}inline int manhattan(int p){    return abs(aim.v[0] - kdt[p].v[0]) +            abs(aim.v[1] - kdt[p].v[1]);}int far, near; // 最远距离、最近距离/* 最远距离的估价 */int dis_b(int p){    int res = 0;    for(int i = 0; i < D; ++i)        res += max(            abs(aim.v[i] - big[p][i]),            abs(aim.v[i] - sml[p][i])        );    return res;}void query_b(int p){    far = max(far, manhattan(p));    int dl = 0, dr = 0;    if(~lc[p])        dl = dis_b(lc[p]);    if(~rc[p])        dr = dis_b(rc[p]);    if(dl > dr)    {        if(dl > far)            query_b(lc[p]);        if(dr > far)            query_b(rc[p]);    }    else    {        if(dr > far)            query_b(rc[p]);        if(dl > far)            query_b(lc[p]);    }}/* 最近距离的估价 */int dis_s(int p){    int res = 0;    for(int i = 0; i < D; ++i)        if(aim.v[i] > big[p][i]) // 超出上边界            res += aim.v[i] - big[p][i];        else if(aim.v[i] < sml[p][i]) // 超出下边界            res += sml[p][i] - aim.v[i];    return res;}void query_s(int p){    // 最近距离不包括到自己的距离    if(int d = manhattan(p))        near = min(near, d);    int dl = X + Y, dr = X + Y;    if(~lc[p])        dl = dis_s(lc[p]);    if(~rc[p])        dr = dis_s(rc[p]);    if(dl < dr)    {        if(dl < near)            query_s(lc[p]);        if(dr < near)            query_s(rc[p]);    }    else    {        if(dr < near)            query_s(rc[p]);        if(dl < near)            query_s(lc[p]);    }}int main(){    ios::sync_with_stdio(false);    cin.tie(0);    int n;    cin >> n;    for(int i = 0; i < n; ++i)        cin >> kdt[i].v[0] >> kdt[i].v[1];    int rt = build(0, n - 1, 0), ans = X + Y;    for(int i = 0; i < n; ++i)    {        aim = kdt[i];        far = 0;        near = X + Y;        query_b(rt);        query_s(rt);        ans = min(ans, far - near);    }    cout << ans << '\n';    return 0;}
原创粉丝点击