这一块知识点也不太好理解。。看了几个小时,大概是懂了,用于范围搜索确实可以提高不少效率
这里有个模板题:https://anoxiacxy.github.io/more/bzoj/p/2648.html
上面那个是二维的,这个算法可以搜索多维的
这里有个讲的还可以https://leileiluoluo.com/posts/kdtree-algorithm-and-implementation.html。。主要理解图,就是关于搜索范围这里可能不太好理解,在代码里面可能有点模糊
下面是上面那个模板题的模板代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 500050;
const int inf = 1e9;
inline int read(){
int x = 0,f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x<<3) + (x<<1) + (ch^'0'); ch = getchar();}
return x * f;
}
struct node{
int d[2]; //坐标
int l,r; //每个点的左(上)区域边界点和右(下)区域边界点
int Max[2]; //这个点所维护的矩形区域
int Min[2];
}t[MAXN];
int op,x,y; //操作类型,坐标
int N,M; //初始化树的结点个数,操作次数
int root,cmpd; //kd树根,操作维度(0表示1维,1表示2维)
int ans; //最近距离
inline bool cmp(node a,node b){
if(a.d[cmpd] != b.d[cmpd])
return a.d[cmpd] < b.d[cmpd];
else
return a.d[!cmpd] < b.d[!cmpd];
}
void kd_push_up(int p){ //更新维护的区域,之所以要这样,是因为维护的是一个正方形区域,或一个圆形区域
if(t[p].l){
if(t[p].Max[0] < t[t[p].l].Max[0])
t[p].Max[0] = t[t[p].l].Max[0];
if(t[p].Min[0] > t[t[p].l].Min[0])
t[p].Min[0] = t[t[p].l].Min[0];
if(t[p].Max[1] < t[t[p].l].Max[1])
t[p].Max[1] = t[t[p].l].Max[1];
if(t[p].Min[1] > t[t[p].l].Min[1])
t[p].Min[1] = t[t[p].l].Min[1];
}
if(t[p].r){
if(t[p].Max[0] < t[t[p].r].Max[0])
t[p].Max[0] = t[t[p].r].Max[0];
if(t[p].Min[0] > t[t[p].r].Min[0])
t[p].Min[0] = t[t[p].r].Min[0];
if(t[p].Max[1] < t[t[p].r].Max[1])
t[p].Max[1] = t[t[p].r].Max[1];
if(t[p].Min[1] > t[t[p].r].Min[1])
t[p].Min[1] = t[t[p].r].Min[1];
}
}
/**
* kd_tree_build(l,r,d):创建kd树
*/
int kd_tree_build(int l,int r,int d){
int mid = (l+r) / 2;
cmpd = d; //因为cmp函数要用cmpd,所以要更新cmpd
nth_element(t+l,t+mid,t+r+1,cmp); //快排
//这里初始化可以在输入的时候完成
t[mid].Max[0] = t[mid].Min[0] = t[mid].d[0];
t[mid].Max[1] = t[mid].Min[1] = t[mid].d[1];
if(l != mid)
t[mid].l = kd_tree_build(l,mid-1,!d);
if(r != mid)
t[mid].r = kd_tree_build(mid+1,r,!d);
kd_push_up(mid); //这里要记得更新维护区域
return mid;
}
/**
* kd_insert(now):插入
*/
void kd_insert(int now){
int D = 0,p = root; //D表示维度,p是根,因为根是从第一维开始排序
while(1){
//更新区域
if(t[p].Max[0] < t[now].Max[0])
t[p].Max[0] = t[now].Max[0];
if(t[p].Min[0] > t[now].Min[0])
t[p].Min[0] = t[now].Min[0];
if(t[p].Max[1] < t[now].Max[1])
t[p].Max[1] = t[now].Max[1];
if(t[p].Min[1] > t[now].Min[1])
t[p].Min[1] = t[now].Min[1];
if(t[now].d[D] >= t[p].d[D]){
if(t[p].r == 0){
t[p].r = now;
return;
}else
p = t[p].r;
}
else{
if(t[p].l == 0){
t[p].l = now;
return;
}
else
p = t[p].l;
}
D = !D;
}
}
inline int dist(int p,int px,int py){
int dis = 0;
if(px < t[p].Min[0])
dis += t[p].Min[0] - px;
if(px > t[p].Max[0])
dis += px - t[p].Max[0];
if(py < t[p].Min[1])
dis += t[p].Min[1] - py;
if(py > t[p].Max[1])
dis += py - t[p].Max[1];
return dis;
}
void kd_query(int p){
int dl,dr,d0;
d0 = abs(t[p].d[0] - x) + abs(t[p].d[1] - y);
if(d0 < ans)
ans = d0;
if(t[p].l)
dl = dist(t[p].l,x,y);
else
dl = inf;
if(t[p].r)
dr = dist(t[p].r,x,y);
else
dr = inf;
if(dl < dr){
if(dl < ans)
kd_query(t[p].l);
if(dr < ans)
kd_query(t[p].r);
}else{
if(dr < ans)
kd_query(t[p].r);
if(dl < ans)
kd_query(t[p].l);
}
}
int main(){
N = read(),M = read();
for(int i = 1;i<=N;i++){
t[i].d[0] = read();
t[i].d[1] = read();
}
root = kd_tree_build(1,N,0);
while(M--){
op = read(),x = read(),y = read();
if(op == 1){
++N;
t[N].Max[0] = t[N].Min[0] = t[N].d[0] = x;
t[N].Max[1] = t[N].Min[1] = t[N].d[1] = y;
kd_insert(N);
}else{
ans = inf;
kd_query(root);
printf("%d\n", ans);
}
}
system("pause");
return 0;
}