题意:给出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;
}