HDU 5992: KD-Tree

来源:互联网 发布:java程序用什么编写 编辑:程序博客网 时间:2024/05/21 02:49

题意:给出N个酒店,每个酒店用平面上一个整点表示,每个酒店有一个价格C。现在有若干次询问,每次询问(x,y,c),求出距离(x,y)最近的且价格不超过C的酒店。


题解:这题要是在比赛上,绝对直接按照C排序,然后离线暴力。但是正解也是要学习的,这个题的正解是kd-Tree,就是所谓的多维度二叉搜索树,众所周知,二叉搜索树有很多种,主要都在贯彻一个思想:降低树的高度,提高搜索效率。那么kd-Tree有多维,如果直接按照多元组的排序来构建二叉树的话,对搜索效率没有任何提升(无法剪枝)。


kd-Tree做出的一个改变就是:每次选取k维中的一维来做关键字来划分左右儿子。再结合降低高度的需求,我们可以选取这k维中方差最大的一维来做关键字,这样这一维数据的差别很大,那么左右儿子的差别响应也就比较大,于是就有很大希望可以剪枝。


关于剪枝:将每个点想象成一个k维空间的点,仿照三维空间中,三维的东西叫做体,二位的东西叫做面,一维的东西叫做线,那么在k维空间中,k-1维的东西就是“面”。假设在某个点是由X维来作为关键词,那么就相当于用mid位置的数据做一个“面”,左儿子的所有点都分布在这个“面”的“左边”,右儿子的所有点都分布在“右边”。而假设询问的点分布在右侧,那么我们可以知道,面左侧的点到询问点的理论最小距离是询问点到“面”的“距离”(k维空间的距离),那么我们可以先把这个理论最小距离算出来,然后我们先搜询问点所在的右侧这一个儿子,然后得到了答案之后,如果比这个理论最小距离小,那么左儿子可以直接剪枝,相反,就还是得搜一下左儿子。


由于使用了方差最大的维度来做每一层的关键词,所以说剪枝的可能是很大的,虽然理论上最坏的复杂度还是o(N),但是想要造出卡kd-Tree的数据也是需要下一番狠功夫的。所以一般来讲可以近似的认为kd-Tree的搜索效率为logn级别的。如果说真的遇到了卡kd-Tree的数据。。。。我觉得可以仿照treap那样子每一层加一个随机因子卡一卡就能过掉(口胡)。


Code:

#include<bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 2e5+100;const LL INF = 0x3f3f3f3f3f3f3f3fLL;int m,n;const int demension = 2;struct Hotel{int pos[demension],id,c;}hotel[maxn],kdtree[maxn];double var[demension];int split [maxn];int cmpDem;bool cmp(const Hotel &a,const Hotel &b){return a.pos[cmpDem]<b.pos[cmpDem];}void build (int l,int r){if (l>=r)return;int mid = l+r >>1;for (int i=0;i<demension;i++){double ave =0;for (int j=l;j<=r;j++){ave+=hotel[j].pos[i];}ave/=(r-l+1);var[i] =0;for (int j=l;j<=r;j++){var[i]+=pow(hotel[j].pos[i]-ave,2);}var[i]/=(r-l+1);}split[mid] =-1;double maxVar=-1;for (int i=0;i<demension;i++){if (var[i]>maxVar){maxVar = var[i];split[mid] =i;}}cmpDem = split[mid];nth_element(hotel+l,hotel+mid,hotel+r+1,cmp);build (l,mid-1);build (mid+1,r);}int ansIndex;LL ansDis;void query(int l,int r,const Hotel& x){if (l>r)return ;int mid = l+r >>1;LL dis =0;for (int i=0;i<demension;i++){dis +=1LL*(x.pos[i]-hotel[mid].pos[i])*(x.pos[i]-hotel[mid].pos[i]);}if (hotel[mid].c<=x.c){if (ansDis == dis && hotel[mid].id<hotel[ansIndex].id){ansIndex = mid;}else if (dis<ansDis){ansDis = dis;ansIndex = mid;} }int d = split[mid];LL radius = 1LL*(x.pos[d]-hotel[mid].pos[d])*(x.pos[d]-hotel[mid].pos[d]);if (x.pos[d]<hotel[mid].pos[d]){query(l,mid-1,x);if (ansDis>radius){query(mid+1,r,x);}}else{query(mid+1,r,x);if (ansDis>radius){query(l,mid-1,x);}}}int T;void input(){scanf("%d%d",&n,&m);for (int i=0;i<n;i++){scanf("%d%d%d",&hotel[i].pos[0],&hotel[i].pos[1],&hotel[i].c);hotel[i].id=i;}build (0,n-1);}void solve(){Hotel x;for (int i=1;i<=m;i++){scanf("%d%d%d",&x.pos[0],&x.pos[1],&x.c);ansDis = INF;ansIndex =n+1;query(0,n-1,x);printf("%d %d %d\n",hotel[ansIndex].pos[0],hotel[ansIndex].pos[1],hotel[ansIndex].c);}}int main(){scanf("%d",&T);while (T--){input();solve();}return 0;}