BZOJ2648 SJY摆棋子

题意:

这天,SJY显得无聊。在家自己玩。在一个棋盘上,有N个黑色棋子。他每次要么放到棋盘上一个黑色棋子,要么放上一个白色棋子,如果是白色棋子,他会找出距离这个白色棋子最近的黑色棋子。此处的距离是 曼哈顿距离 即(|x1-x2|+|y1-y2|) 。现在给出N<=500000个初始棋子。和M<=500000个操作。对于每个白色棋子,输出距离这个白色棋子最近的黑色棋子的距离。同一个格子可能有多个棋子。

思路:kdtree第一题。

kdtree是个什么东西呢?

比如现在有一些二维平面上的点,我们按照维度首先进行一次划分:

比如第一次划分x维,那么我们找到所有点中x坐标为中位数的点,将其作为根,x坐标小于中位数的点划分到左子树,否则划分到右子树。然后递归划分剩余的两侧的点。下一次划分依据为y维,再下一次划分依据为x维。以此类推。

每个节点存储的信息包含为以这个点为根的子树所有的点的矩形的左上角和右下角的坐标。我们在划分结束时更新即可。


那么我们如何查询到一个给定坐标(x,y)曼哈顿距离最近的点呢?

记录全局变量ans,表示目前的最小距离。初值设为INF.

从根节点开始,递归向下查询:首先用当前节点更新答案,随后看左右子树所对应的矩形,若询问点在矩形外,矩形距离的询问点到这个矩形的最近距离,否则为0.

我们优先选择矩形距离比较小的一侧递归向下询问。询问之后,若另一侧的矩形距离不大于当前的最优解,则再询问另一侧。

据神犇说这个过程是最坏O(sqrt(n))的。


考虑插入。

维护一下目前划分的依据是哪一维。

从根开始递归向下插入。

首先用插入点更新当前节点,然后判断一下是应该插入到当前节点的左子树还是右子树,若插入的部分为空,直接插入;否则再递归插入。


那么还有最后一个问题,那就是kdtree的主体必须用指针实现,否则在大数组中的移动异常缓慢,查询和修改都要TLE.


Code:(指针版)

#include <cstdio>
#include <cstring>
#include <climits>
#include <iostream>
#include <algorithm>
using namespace std;
 
#define Min(a, b) ((a)<(b)?(a):(b))
#define Max(a, b) ((a)>(b)?(a):(b))
#define Abs(x) ((x)>0?(x):-(x))
 
#define N 500010
#define M 500010
int n, m;
 
struct Point {
    int x, y;
    Point(int _x = 0, int _y = 0):x(_x),y(_y){}
    void set(int _, int __) {
        x = _, y = __;
    }
}P[N + M];
int Dis(const Point &A, const Point &B) {
    return Abs(A.x - B.x) + Abs(A.y - B.y);
}
 
bool sign;
inline bool cmp(const Point &A, const Point &B) {
    if (sign)
        return A.x < B.x || (A.x == B.x && A.y < B.y);
    else
        return A.y < B.y || (A.y == B.y && A.x < B.x);
}
 
struct Node {
    Node *l, *r;
    int x[2], y[2];
    Point p;
     
    void SetP(const Point &P) {
        p = P;
        x[0] = x[1] = P.x;
        y[0] = y[1] = P.y;
    }
    int Dis(const Point &p) const {
        int res = 0;
        if (p.x < x[0] || p.x > x[1])
            res += (p.x < x[0]) ? x[0] - p.x : p.x - x[1];
        if (p.y < y[0] || p.y > y[1])
            res += (p.y < y[0]) ? y[0] - p.y : p.y - y[1];
        return res;
    }
    void up(Node *B) {
        x[0] = Min(x[0], B->x[0]);
        x[1] = Max(x[1], B->x[1]);
        y[0] = Min(y[0], B->y[0]);
        y[1] = Max(y[1], B->y[1]);
    }
}mem[N + M], *C = mem, Tnull, *null = &Tnull;
 
Node *Build(int tl, int tr, bool d) {
    if (tl > tr)
        return null;
     
    int mid = (tl + tr) >> 1;
    sign = d;
    std::nth_element(P + tl + 1, P + mid + 1, P + tr + 1, cmp);
     
    Node *q = C++;
    q->SetP(P[mid]);
    q->l = Build(tl, mid - 1, d ^ 1);
    q->r = Build(mid + 1, tr, d ^ 1);
    if (q->l != null)
        q->up(q->l);
    if (q->r != null)
        q->up(q->r);
    return q;
}
 
#define INF 0x3f3f3f3f
int res;
void Ask(Node *q, const Point &p) {
    res = Min(res, Dis(q->p, p));
    int DisL = q->l != null ? q->l->Dis(p) : INF;
    int DisR = q->r != null ? q->r->Dis(p) : INF;
    if (DisL < DisR) {
        if (q->l != null)
            Ask(q->l, p);
        if (DisR < res && q->r != null)
            Ask(q->r, p);
    }
    else {
        if (q->r != null)
            Ask(q->r, p);
        if (DisL < res && q->l != null)
            Ask(q->l, p);
    }
}
 
void Insert(Node *root, const Point &p) {
    Node *q = C++;
    q->l = q->r = null;
    q->SetP(p);
     
    sign = 0;
    while(1) {
        root->up(q);
        if (cmp(q->p, root->p)) {
            if (root->l == null) {
                root->l = q;
                break;
            }
            else
                root = root->l;
        }
        else {
            if (root->r == null) {
                root->r = q;
                break;
            }
            else
                root = root->r;
        }
        sign ^= 1;
    }
}
 
int main() {
    #ifndef ONLINE_JUDGE
    freopen("tt.in", "r", stdin);
    freopen("tt.out", "w", stdout);
    #endif
     
    scanf("%d%d", &n, &m);
     
    register int i;
    int ope, x, y;
    for(i = 1; i <= n; ++i) {
        scanf("%d%d", &x, &y);
        P[i] = Point(x, y);
    }
     
    Node* root = Build(1, n, 0);
     
    while(m--) {
        scanf("%d%d%d", &ope, &x, &y);
        if (ope == 1)
            Insert(root, Point(x, y));
        else {
            res = INF;
            Ask(root, Point(x, y));
            printf("%d\n", res);
        }
    }
     
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值