KD-Tree总结

问题引入

平面上有\(n\)个点,\(q\)组询问,每一次查询距离\((x,y)\)最近的点对,强制在线。

问题解决

暴力

显然我们可以直接枚举点然后算距离取\(min\),这样子复杂度是\(\Theta(nq)\)的。

KD-Tree

而\(KD-Tree\)就是一个解决这种问题的利器

我们不妨从这个平面中选出一些点把平面分割成两个部分,那么所有的点就会在一段范围内对吧。我们只需要暴力的找每一段里面的即可。

但是这样子复杂度还是不对,还是\(\Theta(nq)\)的,此时我们需要把每一块区域的范围给算出来,判断边界到这个点的距离是不是比\(ans\)小,即搜索的过程中进行乐观估价剪枝。

当然还可以加一些搜索顺序的优化,具体实现参见代码。

至此,\(KD-Tree\)的大致流程就讲完了,虽然我觉得我自己都看着一脸懵逼,但是结合代码你可能可以获得更好的阅读体验。

更进一步

如果这个时候可以插入点或者删除点呢?

插入

直接暴力插进去然后按照替罪羊树那套理论重构即可。

删除

打个惰性删除标记然后替罪羊树那套理论重构即可。

例题

P2479 [SDOI2010]捉迷藏

直接查即可,注意不能和当前点重复。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define ll long long
#define REP(a,b,c) for(int a=b;a<=c;a++)
#define re register
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
inline int gi(){
int f=1,sum=0;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return f*sum;
}
const int N=500010,Inf=1e9+10;
int rt,n,tot,now,ans1,ans2;
struct node{int x[2];bool operator!=(const node &a)const{return (x[0]!=a.x[0]) || (x[1]!=a.x[1]);}}a[N];
struct tree{int ls,rs,mn[2],mx[2];node w;}t[N];
int newnode(){return ++tot;}
bool cmp(node a,node b){return a.x[now]<b.x[now];}
void update(int o){
for(int i=0;i<2;i++){
t[o].mx[i]=t[o].mn[i]=t[o].w.x[i];
if(t[o].ls)t[o].mx[i]=max(t[o].mx[i],t[t[o].ls].mx[i]),t[o].mn[i]=min(t[o].mn[i],t[t[o].ls].mn[i]);
if(t[o].rs)t[o].mx[i]=max(t[o].mx[i],t[t[o].rs].mx[i]),t[o].mn[i]=min(t[o].mn[i],t[t[o].rs].mn[i]);
}
}
int build(int l,int r,int opt){
if(l>r)return 0;
int mid=(l+r)>>1,o=newnode();now=opt;
nth_element(a+l,a+mid,a+r+1,cmp);t[o].w=a[mid];
t[o].ls=build(l,mid-1,opt^1);t[o].rs=build(mid+1,r,opt^1);
update(o);return o;
}
int getmin(int o,node now){
int ret=0;
for(int i=0;i<2;i++)ret+=max(0,now.x[i]-t[o].mx[i])+max(0,t[o].mn[i]-now.x[i]);
return ret;
}
int getmax(int o,node now){
int ret=0;
for(int i=0;i<2;i++)ret+=max(abs(now.x[i]-t[o].mx[i]),abs(now.x[i]-t[o].mn[i]));
return ret;
}
int dis(node a,node b){return abs(a.x[0]-b.x[0])+abs(a.x[1]-b.x[1]);}
void query_max(int o,node now){
if(t[o].w!=now)ans1=max(ans1,dis(t[o].w,now));
int le=-Inf,ri=-Inf;
if(t[o].ls)le=getmax(t[o].ls,now);
if(t[o].rs)ri=getmax(t[o].rs,now);
if(le>ri){
if(le>ans1)query_max(t[o].ls,now);
if(ri>ans1)query_max(t[o].rs,now);
}
else{
if(ri>ans1)query_max(t[o].rs,now);
if(le>ans1)query_max(t[o].ls,now);
}
}
void query_min(int o,node now){
if(t[o].w!=now)ans2=min(ans2,dis(t[o].w,now));
int le=Inf,ri=Inf;
if(t[o].ls)le=getmin(t[o].ls,now);
if(t[o].rs)ri=getmin(t[o].rs,now);
if(le<ri){
if(le<ans2)query_min(t[o].ls,now);
if(ri<ans2)query_min(t[o].rs,now);
}
else{
if(ri<ans2)query_min(t[o].rs,now);
if(le<ans2)query_min(t[o].ls,now);
}
}
int main(){
n=gi();
for(int i=1;i<=n;i++)a[i].x[0]=gi(),a[i].x[1]=gi();
rt=build(1,n,0);int ans=Inf;
for(int i=1;i<=n;i++){
ans1=-Inf,ans2=Inf;
query_max(rt,a[i]);query_min(rt,a[i]);
ans=min(ans,ans1-ans2);
}
printf("%d\n",ans);
return 0;
}

例题2

P4169 [Violet]天使玩偶/SJY摆棋子

插入的模板题,按照上文所述的方法做即可。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define ll long long
#define REP(a,b,c) for(int a=b;a<=c;a++)
#define re register
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
inline int gi(){
int f=1,sum=0;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return f*sum;
}
const int N=2000010,Inf=1e9+10;
const double alpha=0.75;
struct node{int x[2];}a[N];
struct tree{int mx[2],mn[2],siz,ls,rs;node w;}t[N];
int n,m,rt,tot,now,ans;
int sta[N],top;
int newnode(){if(top)return sta[top--];else return ++tot;}
bool cmp(node a,node b){return a.x[now]<b.x[now];}
void update(int o){
for(int i=0;i<2;i++){
t[o].mx[i]=t[o].mn[i]=t[o].w.x[i];
if(t[o].ls)t[o].mx[i]=max(t[o].mx[i],t[t[o].ls].mx[i]),t[o].mn[i]=min(t[o].mn[i],t[t[o].ls].mn[i]);
if(t[o].rs)t[o].mx[i]=max(t[o].mx[i],t[t[o].rs].mx[i]),t[o].mn[i]=min(t[o].mn[i],t[t[o].rs].mn[i]);
}
t[o].siz=t[t[o].ls].siz+t[t[o].rs].siz+1;
}
int build(int l,int r,int opt){
if(l>r)return 0;
int mid=(l+r)>>1,o=newnode();now=opt;
nth_element(a+l,a+mid,a+r+1,cmp);t[o].w=a[mid];
t[o].ls=build(l,mid-1,opt^1);t[o].rs=build(mid+1,r,opt^1);
update(o);return o;
}
void get(int o,int cnt){
if(t[o].ls)get(t[o].ls,cnt);
a[cnt+t[t[o].ls].siz+1]=t[o].w;sta[++top]=o;
if(t[o].rs)get(t[o].rs,cnt+t[t[o].ls].siz+1);
}
void check(int &o,int opt){
if(t[o].siz*alpha<t[t[o].ls].siz || t[o].siz*alpha<t[t[o].rs].siz)
get(o,0),o=build(1,t[o].siz,opt);
}
void insert(int &o,node now,int opt){
if(!o){o=newnode();t[o].w=now;t[o].ls=t[o].rs=0;update(o);return;}
if(now.x[opt]<=t[o].w.x[opt])insert(t[o].ls,now,opt^1);
else insert(t[o].rs,now,opt^1);
update(o);check(o,opt);
}
int getdis(node now,int o){
int ret=0;
for(int i=0;i<2;i++)ret+=max(now.x[i]-t[o].mx[i],0)+max(t[o].mn[i]-now.x[i],0);
return ret;
}
int dis(node a,node b){return abs(a.x[0]-b.x[0])+abs(a.x[1]-b.x[1]);}
void query(int o,node now){
ans=min(ans,dis(t[o].w,now));
int le=Inf,ri=Inf;
if(t[o].ls)le=getdis(now,t[o].ls);
if(t[o].rs)ri=getdis(now,t[o].rs);
if(le<ri){
if(le<ans)query(t[o].ls,now);
if(ri<ans)query(t[o].rs,now);
}
else{
if(ri<ans)query(t[o].rs,now);
if(le<ans)query(t[o].ls,now);
}
}
int main(){
n=gi();m=gi();
for(int i=1;i<=n;i++)a[i].x[0]=gi(),a[i].x[1]=gi();
rt=build(1,n,0);
while(m--){
int opt=gi();node now;now.x[0]=gi();now.x[1]=gi();
if(opt==1)insert(rt,now,0);
else{ans=Inf;query(rt,now);printf("%d\n",ans);}
}
return 0;
}

参考文献

儿子的Blog

感谢儿子对我的滋磁!

KD-Tree总结的更多相关文章

  1. AOJ DSL_2_C Range Search (kD Tree)

    Range Search (kD Tree) The range search problem consists of a set of attributed records S to determi ...

  2. k-d tree 学习笔记

    以下是一些奇怪的链接有兴趣的可以看看: https://blog.sengxian.com/algorithms/k-dimensional-tree http://zgjkt.blog.uoj.ac ...

  3. 【BZOJ-2648&2716】SJY摆棋子&天使玩偶 KD Tree

    2648: SJY摆棋子 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 2459  Solved: 834[Submit][Status][Discu ...

  4. K-D Tree

    这篇随笔是对Wikipedia上k-d tree词条的摘录, 我认为解释得相当生动详细, 是一篇不可多得的好文. Overview A \(k\)-d tree (short for \(k\)-di ...

  5. K-D Tree题目泛做(CXJ第二轮)

    题目1: BZOJ 2716 题目大意:给出N个二维平面上的点,M个操作,分为插入一个新点和询问到一个点最近点的Manhatan距离是多少. 算法讨论: K-D Tree 裸题,有插入操作. #inc ...

  6. k-d Tree in TripAdvisor

    Today, TripAdvisor held a tech talk in Columbia University. The topic is about k-d Tree implemented ...

  7. k-d tree算法

    k-d树(k-dimensional树的简称),是一种分割k维数据空间的数据结构.主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索). 应用背景 SIFT算法中做特征点匹配的时候就会利用到k ...

  8. k-d tree模板练习

    1. [BZOJ]1941: [Sdoi2010]Hide and Seek 题目大意:给出n个二维平面上的点,一个点的权值是它到其他点的最长距离减最短距离,距离为曼哈顿距离,求最小权值.(n< ...

  9. [模板] K-D Tree

    K-D Tree K-D Tree可以看作二叉搜索树的高维推广, 它的第 \(k\) 层以所有点的第 \(k\) 维作为关键字对点做出划分. 为了保证划分均匀, 可以以第 \(k\) 维排名在中间的节 ...

  10. BZOJ3489 A simple rmq problem K-D Tree

    传送门 什么可持久化树套树才不会写呢,K-D Tree大法吼啊 对于第\(i\)个数,设其前面最后的与它值相同的位置为\(pre_i\),其后面最前的与它值相同的位置为\(aft_i\),那么对于一个 ...

随机推荐

  1. Java的Annnotation (注解)

    注解是什么呢? 其实就像商场的商品上都贴有自己的标签一样,它提供了关于这个商品的许多额外信息.你可以根据这些信息对其进行附加的处理. (Java的语法糖果然比较差劲), 这个name()方法太累赘了, ...

  2. 千万不要用window自带文本编辑器编辑配置文件或者代码

    1 引言 用windows自带的text文本在最前面会带入看不到的BOM,导致异常 2 代码 package main import ( "strings" "fmt&q ...

  3. JavaWeb分页-----PageBean.java

    package com.zzuli.util; import java.util.List; /** * PageBean类 * @author hejjon * @date 2019年6月8日 下午 ...

  4. 前端1-----块级标签(独占一行),排版标签(样式排版),其他标签,form表单(input的多种类型)

    前端1-----块级标签(独占一行),排版标签(样式排版),其他标签,form表单(input的多种类型) 一丶HTML块级标签 排版标签 p 标签: 段落标签,会自动在段落上下加上空白来分开 p标签 ...

  5. English--定语从句

    English|定语从句 从介绍从句开始,英语的句子已经开始逐渐复杂了!做好心理准备,三大从句介绍完毕,会介绍分词短语.废话不多说,直接开始干! 前言 目前所有的文章思想格式都是:知识+情感. 知识: ...

  6. HTML不换行,多余用省略号代替

    最主要的css样式: white-space: nowrap; overflow: hidden; text-overflow: ellipsis; 例子: <!DOCTYPE html> ...

  7. 回调、Promise、async-await

    第一章 异步:现在与将来 程序中现在运行的部分和将来运行的部分之间的关系就是异步编程的核心. 场景:等待用户输入.从数据库或文件系统中请求数据.通过网络 发送数据并等待响应,或者是在以固定时间间隔执行 ...

  8. HTML 注释 和 实体字符

    一.注释 在HTML中还有一种特殊的标签——注释标签.如果需要在HTML文档中添加一些便于阅读和理解但又不需要显示在页面中的注释文字,就需要使用注释标签. 注释内容不会显示在浏览器窗口中,但是作为HT ...

  9. springBoot 发布war/jar包到tomcat(idea)

    参考链接:https://blog.csdn.net/qq1076472549/article/details/81318729 1.启动类目录新增打包类:  2.pom.xml新增依赖:<pa ...

  10. tp5 宝塔open_basedir restriction in effect 错误; IIS open_basedir restriction in effect

    很久前做过的一个微信项目,客户突然找到我说换了部署环境后网站报错,再跟客户确定了php版本,伪静态设置后,网站依旧打不开,官网手册这样解释: 然而因为客户是iis8的表示该文档一点鸡毛用都米有哇,求助 ...