BZOJ2648: SJY摆棋子 && 暴力kdtree学习笔记

1 篇文章 0 订阅

题目

BZOJ2648: SJY摆棋子

题解

其实就是kdt学习笔记。。
把平面上n个点用一种奇怪的方式,弄成一棵树结构,然后保证插入,询问某个点的最近点,的复杂度都不会太高(起码得比暴力优),于是kdt诞生了,其实kdt比这个强大多了,可以查找d维的k近点。
方式是什么?我突然想起一道题叫做,ZJOI2016旅行者。记得横竖的分治方法吗。(没见过这道题的请忽略)。
为了保证树不会太高,最好是log级别的,这里划分树结构的方式是这样的,比如先从x维开始划,找到这堆点的x的中位数,然后根据这个划成两堆,而中间那个作为中位数的点,就成为树这个位置的节点了,并且需要保存它控制的这一堆的矩形边界。然后换成y维继续做剩下的两堆,做完再换成x维balabala。看起来很有理有据,,事实上也的确是这样要不怎么会被发明出来。

下面说一说如何找一个点对应的最近点。首先从根开始,先用这个节点对应的点更新一下ans,然后分别计算一下询问的点到左儿子和右儿子所控制矩形的距离(在矩形内部则为0,不存在则为INF),这是个下界,如果预估出来的值小于ans那么进入这个儿子递归询问,并且优先询问预估值比较小的儿子(这一步可以更新ans,有点像A*搜索,可以剪枝)。不难发现不剪枝是 O(n) 的(虽然剪了据说也能被卡,详见这道题的discuss),剪了之后据说是随机数据期望 O(logn) 的,能被卡到 O(n) (不过能不能被卡到 O(n) )我也不太清楚,,因为貌似没有解释kdt复杂度的文章,如果哪位找到了请一定告诉我谢谢qwq。

如何插入呢,据说是暴力插然后用替罪羊重构的思想,因为yy了一下感觉暴力插可以被卡到 O(n) ,wkl大爷说可以rotate。。(orz),其实这道题暴力插可以过。就判断一下插入点和当前节点某一维大小关系,就是二叉搜索树的插入。

,这题一点都不给 O(nlogn2) 的cdq+线段树活路。

代码

//QWsin
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int INF=1<<29;

const int maxn=500000+10;

inline int read()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0';
    return ret;
}

int n,m;

struct Node{
    int M[2],m[2],d[2];
    Node *ch[2];
    Node(){M[0]=M[1]=d[0]=d[1]=0;m[0]=m[1]=INF;ch[0]=ch[1]=NULL;}
    inline void modify(int dd[2]){M[0]=m[0]=dd[0];M[1]=m[1]=dd[1];d[0]=dd[0];d[1]=dd[1];}
    inline void up(Node *c){
        M[0]=max(M[0],c->M[0]);M[1]=max(M[1],c->M[1]);  
        m[0]=min(m[0],c->m[0]);m[1]=min(m[1],c->m[1]);
    }
}*root;

int xy[maxn][2],id[maxn];//xy[i][0]=x xy[i][1]=y

int D;
inline int cmp(const int &a,const int &b){
    return (xy[a][D] < xy[b][D])||(xy[a][D]==xy[b][D]&&xy[a][D^1] < xy[b][D^1]);
}

void build(Node* &p,int l,int r,int t)
{
    int mid=(l+r)>>1;D=t;
    nth_element(id+l,id+mid,id+r+1,cmp);
    p=new Node();p->modify(xy[id[mid]]);
    if(l!=mid) build(p->ch[0],l,mid-1,t^1),p->up(p->ch[0]);
    if(r!=mid) build(p->ch[1],mid+1,r,t^1),p->up(p->ch[1]);
}

int ite[2];
void insert()
{
    Node *cur=root,*tmp=new Node();tmp->modify(ite);
    for(int D=0;;D^=1)
    {
        cur->up(tmp);
        int t=ite[D] > cur->d[D];
        if(cur->ch[t] == NULL) {cur->ch[t]=tmp;return ;}
        else cur=cur->ch[t];
    }
}

inline int dis(int d[2],Node *p)
{
    int ret=0;
    ret+=(d[0] < p->m[0]) ? p->m[0]-d[0] : 0;
    ret+=(d[0] > p->M[0]) ? d[0]-p->M[0] : 0;
    ret+=(d[1] < p->m[1]) ? p->m[1]-d[1] : 0;
    ret+=(d[1] > p->M[1]) ? d[1]-p->M[1] : 0;
    return ret;
}

int ans;
void query(Node* p)
{
    if(!p) return ;
    ans=min(ans,abs(p->d[0] - ite[0]) + abs(p->d[1] - ite[1]));
    int t0=p->ch[0]?dis(ite,p->ch[0]):INF;
    int t1=p->ch[1]?dis(ite,p->ch[1]):INF;
    if(t0 < t1){
        if(t0 < ans) query(p->ch[0]);
        if(t1 < ans) query(p->ch[1]);
    }
    else{
        if(t1 < ans) query(p->ch[1]);
        if(t0 < ans) query(p->ch[0]);   
    }
}

inline void init_data()
{
    cin>>n>>m;
    for(int i=1;i<=n;++i) xy[i][0]=read(),xy[i][1]=read(),id[i]=i ;
    build(root,1,n,0);
}

inline void solve()
{
    int op;
    while(m--)
    {
        op=read();ite[0]=read();ite[1]=read();
        if(op==1) insert();
        else {
            ans=INF;query(root);
            printf("%d\n",ans);
        }
    }
}

int main()
{
    init_data();
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值