【裸K-D树】BZOJ 2648

Problem Description

棋盘上原本有,n个黑棋。
有两种操作:
1 x y 插入一个黑色棋子
2 x y 输出距离这个白色棋子最近的黑色棋子距离(这里的距离是曼哈顿距离

思路:

参考ldq大佬博客
K-D树,其实就是一个k维的搜索树。这道题k = 2而已。

#include<bits/stdc++.h>
using namespace std;
const int DIM = 2;//维度,题目是二维
const int inf = 0x3f3f3f3f;
const int MAX = 500005;
struct node
{
    int l, r;
    int cd[DIM], maxn[DIM], minn[DIM];//分别代表坐标,以这个结点的为根的子树每一维最大值,和每一维最小值
    inline void maintain()//初始化
    {
        l = r = 0;
        for(int i = 0; i < DIM; i++)
            maxn[i] = minn[i] = cd[i];
    }
}tree[2*MAX];
int D;//第几维
bool operator < (const node &a, const node &b)//重载从小到大
{
    return a.cd[D] < b.cd[D];
}
inline void Merge(int mid)//归并,也就是向上更新
{
    //主要就是为了更新该结点maxn[], minn[]
    int son[2] = {tree[mid].l, tree[mid].r};
    for(int i = 0; i < 2; i++)
    {
        if(!son[i]) continue;
        for(int j = 0; j < DIM; j++)
        {
            tree[mid].maxn[j] = max(tree[mid].maxn[j], tree[son[i]].maxn[j]);
            tree[mid].minn[j] = min(tree[mid].minn[j], tree[son[i]].minn[j]);
        }
    }
}
int build(int l, int r, int now)//建树
{
    int mid = (l+r)>>1;
    D = now;
    nth_element(tree+l, tree+mid, tree+r+1);//D维时,以mid这点为中点,左小 右大
    tree[mid].maintain();//初始化
    if(l < mid) tree[mid].l = build(l, mid-1, (now+1)%DIM);
    if(r > mid) tree[mid].r = build(mid+1, r, (now+1)%DIM);
    Merge(mid);//归并,向上更新
    return mid;
}
int partionMin(int o, int k)//求k这个点,到o这个子树所有点,可能的最小距离值
{
    int red = 0;
    for(int i = 0; i < DIM; i++)
    {
        if(tree[k].cd[i] > tree[o].maxn[i]) red += tree[k].cd[i] - tree[o].maxn[i];
        if(tree[k].cd[i] < tree[o].minn[i]) red += tree[o].minn[i] - tree[k].cd[i];
    }
    return red;
}
int ans;
void Query(int o, int k)//查找距离k这个点最近的距离
{
    int dm = abs(tree[o].cd[0] - tree[k].cd[0]) + abs(tree[o].cd[1] - tree[k].cd[1]);//曼哈顿距离
    if(ans > dm) ans = dm;//更新
    int dl = tree[o].l ? partionMin(tree[o].l, k) : inf;//如果左子树存在就求k这个点,到tree[o].l这个子树所有点,可能的最小距离值。否则无穷
    int dr = tree[o].r ? partionMin(tree[o].r, k) : inf;//
    if(dl < dr)//目的优化时间复杂度
    {
        if(dl < ans) Query(tree[o].l, k);
        if(dr < ans) Query(tree[o].r, k);
    }
    else
    {
        if(dr < ans) Query(tree[o].r, k);
        if(dl < ans) Query(tree[o].l, k);
    }
}
void Insert(int &o, int k, int now)//插入操作
{
    if(o == 0)//代表到叶子结点的儿子
    {
        o = k;//更新就好了
        return ;
    }
    if(tree[k].cd[now] < tree[o].cd[now]) Insert(tree[o].l, k, (now+1)%DIM);
    else Insert(tree[o].r, k, (now+1)%DIM);
    Merge(o);
}
int main()
{
    int n, m, i, j;
    while(~scanf("%d %d", &n, &m))
    {
        for(i = 1; i <= n; i++)
            for(j = 0; j < DIM; j++)
            scanf("%d", &tree[i].cd[j]);
        int pos = n + 1;
        int root = build(1, n, 0);
        int op;
        while(m--)
        {
            scanf("%d", &op);
            for(j = 0; j < DIM; j++) scanf("%d", &tree[pos].cd[j]);
            if(op == 1)
            {
                tree[pos].maintain();
                Insert(root, pos, 0);
                pos++;
            }
            else
            {
                ans = inf;
                Query(root, pos);
                printf("%d\n", ans);
            }

        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值