BZOJ 2648 SJY摆棋子

2648: SJY摆棋子
Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 1631 Solved: 523

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

Input
第一行两个数 N M
以后M行,每行3个数 t x y
如果t=1 那么放下一个黑色棋子
如果t=2 那么放下一个白色棋子
Output
对于每个T=2 输出一个最小距离

Sample Input
2 3
1 1
2 3
2 1 2
1 3 3
2 4 2

Sample Output
1
2

这道题同样是一道kd-tree的题,我大体采用了与之前HDU4347的做法,因此我只介绍不同之处。首先之前HDU4347,N个节点需要对应的kd-tree要4*N个节点,因为它默认第k个节点的左二子在第2k个节点,右儿子在第2k+1个节点,这造成了空间的大量浪费,因为中间有很多格都是空的,因此我在这个程序中,就将左右儿子存在下一个空位,然后再父节点上设两个变量left和right分别指向存放左右儿子的空位。然后这道题相对于HDU4347增加了剪枝,原来是否搜索某当前节点的某一子树判定条件是将查询节点与当前节点比较当前维,如果查询节点在当前维小于等于当前节点,那么搜索当前节点的左子树,相等否则搜索右子树。搜索完该子树之后,判断是否搜索另一子树的条件是在当前维上查询节点与当前节点的距离是否小于当前保存节点中距离查询节点的最远距离,如果是,那么搜索另一子树,否则,不搜索。但在本题中,我们在每个节点保存一个它及它的所有子树中所含节点覆盖的范围,举个例子,例如:总共5个节点(1,1),(2,3),(4,6),(0,5),(5,1), 那么覆盖的范围就是: 0x5 , 1y6 . 因此如果查询节点不在这个范围内,例如查询节点为(-1,-1), 那么我们知道它到这5个节点的距离至少为|-1-0|+|-1-1| = 3, (这道题用的是曼哈顿距离),所以如果当前保存节点中距离查询节点的最远距离小于3,那么我们就不用搜索这5个节点了,因为我们必然找不到一个距离小于3的点。这种方法就比原方法减少了搜索时间,如图:这里写图片描述
查询节点A距离分割线很近,所以使用老的方法,程序很可能就会搜索另一子树(包括C和D),但如果我们用新的方法,我们判断的就是A到C,D围成的矩形的距离,而这个距离就很大,程序很可能不需要搜索这一子树,从而节省了搜索时间。

本题相较于HDU4347还多了一个插入,即在kd-tree建成后,我们还可以在程序运行过程中动态地向kd-tree插入节点,其实过程很简单,就是从根节点开始查询插入节点应该处的位置,找到该位置后把该节点插入到该位置,需要注意的是在这个查询过程中需要沿途更新每个节点及它的子树的覆盖范围,即:如果插入节点在之前的覆盖范围内,则不必更新,否则将会更新为一个更大的覆盖范围。

下面是程序的完整代码:

#include <stdio.h>
#include <queue>
#include <algorithm>
using namespace std;

#define N 500001

short currentDim;
int nextIndex = 0;

struct point
{
    int pos[2];
    int left;
    int right;
    int parent;
    int Min[2], Max[2];

    point()
    {
        left = -1;
        right = -1;
    }
    bool operator < (const point &u) const
    {
        return pos[currentDim] < u.pos[currentDim];
    }

} point_set[2*N];

point kd_tree[2*N];
pair<int, point> nearest_point;

inline void construct_kdTree(int p, int r, int index, short depth)
{
    if(index == -1)
        return;

    if(p <= r)
    {
        currentDim = depth % 2;
        int mid = (p + r) / 2;
        nth_element(point_set+p, point_set+mid, point_set+r+1);
        kd_tree[index] = point_set[mid];
        nextIndex = index+1;

        kd_tree[index].Min[0] = kd_tree[index].Max[0] = kd_tree[index].pos[0];
        kd_tree[index].Min[1] = kd_tree[index].Max[1] = kd_tree[index].pos[1];

        if(p > mid - 1)
            kd_tree[index].left = -1;
        else if(nextIndex < 2*N)
            kd_tree[index].left = nextIndex++;
        else
            kd_tree[index].left = -1;

        construct_kdTree(p, mid-1, kd_tree[index].left, depth+1);

        if(mid + 1 > r)
            kd_tree[index].right = -1;
        else if(nextIndex < 2*N)
            kd_tree[index].right = nextIndex++;
        else
            kd_tree[index].right = -1;

        construct_kdTree(mid+1, r, kd_tree[index].right, depth+1);

        if(kd_tree[index].left != -1)
        {
            int leftChild = kd_tree[index].left;
            if(kd_tree[index].Max[0] < kd_tree[leftChild].Max[0])
                kd_tree[index].Max[0] = kd_tree[leftChild].Max[0];
            if(kd_tree[index].Max[1] < kd_tree[leftChild].Max[1])
                kd_tree[index].Max[1] = kd_tree[leftChild].Max[1];
            if(kd_tree[index].Min[0] > kd_tree[leftChild].Min[0])
                kd_tree[index].Min[0] = kd_tree[leftChild].Min[0];
            if(kd_tree[index].Min[1] > kd_tree[leftChild].Min[1])
                kd_tree[index].Min[1] = kd_tree[leftChild].Min[1];
        }
        if(kd_tree[index].right != -1)
        {
            int rightChild = kd_tree[index].right;
            if(kd_tree[index].Max[0] < kd_tree[rightChild].Max[0])
                kd_tree[index].Max[0] = kd_tree[rightChild].Max[0];
            if(kd_tree[index].Max[1] < kd_tree[rightChild].Max[1])
                kd_tree[index].Max[1] = kd_tree[rightChild].Max[1];
            if(kd_tree[index].Min[0] > kd_tree[rightChild].Min[0])
                kd_tree[index].Min[0] = kd_tree[rightChild].Min[0];
            if(kd_tree[index].Min[1] > kd_tree[rightChild].Min[1])
                kd_tree[index].Min[1] = kd_tree[rightChild].Min[1];
        }
    }
}

inline int dist(int index, point p)
{
    int distance  = 0;
    if(p.pos[0] < kd_tree[index].Min[0])
        distance += kd_tree[index].Min[0] - p.pos[0];
    if(p.pos[0] > kd_tree[index].Max[0])
        distance += p.pos[0] - kd_tree[index].Max[0];
    if(p.pos[1] < kd_tree[index].Min[1])
        distance += kd_tree[index].Min[1] - p.pos[1];
    if(p.pos[1] > kd_tree[index].Max[1])
        distance += p.pos[1] - kd_tree[index].Max[1];
    return distance;
}

inline void query_kdTree(point p, int index, short depth)
{
    if(index == -1)
        return;
    pair<int, point> current_node(0, kd_tree[index]);
    current_node.first = abs(p.pos[0]-current_node.second.pos[0])
        + abs(p.pos[1]-current_node.second.pos[1]);

    if(current_node.first < nearest_point.first)
    {
        nearest_point.first = current_node.first;
        nearest_point.second = current_node.second;
    }

    int lchild = kd_tree[index].left;
    int rchild = kd_tree[index].right;

    if(lchild != -1 && rchild != -1)
    {
        int ldist = dist(lchild, p);
        int rdist = dist(rchild, p);
        if(ldist < rdist)
        {
            if(ldist < nearest_point.first)
                query_kdTree(p, lchild, depth+1);
            if(rdist < nearest_point.first)
                query_kdTree(p, rchild, depth+1);
        }
        else
        {
            if(rdist < nearest_point.first)
                query_kdTree(p, rchild, depth+1);
            if(ldist < nearest_point.first)
                query_kdTree(p, lchild, depth+1);
        }
    }
    else if(lchild != -1)
    {
        int ldist = dist(lchild,p);
        if(ldist < nearest_point.first)
            query_kdTree(p, lchild, depth+1);
    }
    else if(rchild != -1)
    {
        int rdist = dist(rchild,p);
        if(rdist < nearest_point.first)
            query_kdTree(p, rchild, depth+1);
    }
}

inline void insert_kdTree(point p, int index, int parent, short depth, int rightorleft)  //ys: rightofleft 0 is left, 1 is right
{
    if(index == -1 && nextIndex < 2*N)
    {
        kd_tree[nextIndex].pos[0] = p.pos[0];
        kd_tree[nextIndex].pos[1] = p.pos[1];
        kd_tree[nextIndex].parent = parent;
        kd_tree[nextIndex].Min[0] = kd_tree[nextIndex].Max[0] = kd_tree[nextIndex].pos[0];
        kd_tree[nextIndex].Min[1] = kd_tree[nextIndex].Max[1] = kd_tree[nextIndex].pos[1];

        if(rightorleft)  //ys: right child
            kd_tree[parent].right = nextIndex;
        else
            kd_tree[parent].left = nextIndex;

        nextIndex++;
        return;
    }

    if(kd_tree[index].pos[0] == p.pos[0] && kd_tree[index].pos[1] == p.pos[1]) //ys: don't need to insert repeated value
    {
        return;
    }

    if(kd_tree[index].Max[0] < p.Max[0])
        kd_tree[index].Max[0] = p.Max[0];
    if(kd_tree[index].Max[1] < p.Max[1])
        kd_tree[index].Max[1] = p.Max[1];
    if(kd_tree[index].Min[0] > p.Min[0])
        kd_tree[index].Min[0] = p.Min[0];
    if(kd_tree[index].Min[1] > p.Min[1])
        kd_tree[index].Min[1] = p.Min[1];

    short idm = depth % 2;
    int lchild = kd_tree[index].left;
    int rchild = kd_tree[index].right;

    if(kd_tree[index].pos[idm] <= p.pos[idm])
        insert_kdTree(p, rchild, index, depth+1, 1);
    else
        insert_kdTree(p, lchild, index, depth+1, 0);

}

int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i = 0; i < n; i++)
    {
        scanf("%d%d",&point_set[i].pos[0],&point_set[i].pos[1]);
    }

    construct_kdTree(0, n-1, 1, 0);

    for(int i = 0; i < m; i++)
    {
        int t;  //ys: 1 is black, 2 is white
        point tmpv;
        scanf("%d%d%d", &t, &tmpv.pos[0], &tmpv.pos[1]);
        tmpv.Min[0] = tmpv.Max[0] = tmpv.pos[0];
        tmpv.Min[1] = tmpv.Max[1] = tmpv.pos[1];

        if(t == 2)   //ys: white
        {
            nearest_point.first = 99999999;
            query_kdTree(tmpv, 1, 0);
            printf("%d\n", nearest_point.first);
        }
        else
        {
            if(kd_tree[1].pos[0] == tmpv.pos[0] && kd_tree[1].pos[1] == tmpv.pos[1])
                continue;
            if(kd_tree[1].pos[0] < tmpv.pos[0])
                insert_kdTree(tmpv, kd_tree[1].right, 1, 1, 1);
            else
                insert_kdTree(tmpv, kd_tree[1].left, 1, 1, 0);
        }
    }


}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值