题目
题解
其实就是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;
}