大概就是个复杂度对的暴力做法,在你不想写二维线段树等的时候优秀的替代品。

优点:思路简单,代码好写。

他大概有两种用法(虽然差不多)。

在平面坐标系中干一些事情:

例如最常规的平面最近最远点,不管是欧几里得距离还是曼哈顿距离,本质上都是一样的。

利用不同维度的尽量平均的分割,再在询问时剪枝。

这里给出一个曼哈顿距离上的最近最远距离的版本,可供参考:

 namespace KD {
int Rt, lc[N], rc[N], u[N], d[N], l[N], r[N];
inline void Merge(int x, int y) {
u[x] = std::max(u[x], u[y]);
d[x] = std::min(d[x], d[y]);
l[x] = std::min(l[x], l[y]);
r[x] = std::max(r[x], r[y]);
}
inline void Up(int t) {
l[t] = r[t] = p[t].v[];
u[t] = d[t] = p[t].v[];
if (lc[t]) Merge(t, lc[t]);
if (rc[t]) Merge(t, rc[t]);
}
int Build(int l, int r, int dep) {
if (l >= r) {
if (l == r) Up(l);
return (l == r)? (l) : ();
}
Mt = dep & ; int md = (l + r) >> ;
std::nth_element(p + l, p + md, p + + r);
lc[md] = Build(l, md - , dep + );
rc[md] = Build(md + , r, dep + );
Up(md); return md;
}
inline int In_mi(int t) {
int re = ;
re += std::max(qi.v[] - r[t], );
re += std::max(l[t] - qi.v[], );
re += std::max(qi.v[] - u[t], );
re += std::max(d[t] - qi.v[], );
return re;
}
inline int In_ma(int t) {
int re = ;
re += std::max(std::abs(qi.v[] - r[t]), std::abs(qi.v[] - l[t]));
re += std::max(std::abs(qi.v[] - u[t]), std::abs(qi.v[] - d[t]));
return re;
}
void Query_mi(int t, int dep) {
if (!t) return; Mt = dep & ;
if (qi != p[t]) ani = std::min(ani, Dis(qi, p[t]));
int dl = (lc[t])? (In_mi(lc[t])) : (INF);
int dr = (rc[t])? (In_mi(rc[t])) : (INF);
if (dl < dr) {
if (ani > dl) Query_mi(lc[t], dep + );
if (ani > dr) Query_mi(rc[t], dep + );
} else {
if (ani > dr) Query_mi(rc[t], dep + );
if (ani > dl) Query_mi(lc[t], dep + );
}
}
void Query_ma(int t, int dep) {
if (!t) return; Mt = dep & ;
ana = std::max(ana, Dis(qi, p[t]));
int dl = (lc[t])? (In_ma(lc[t])) : ();
int dr = (rc[t])? (In_ma(rc[t])) : ();
if (dl > dr) {
if (ana < dl) Query_ma(lc[t], dep + );
if (ana < dr) Query_ma(rc[t], dep + );
} else {
if (ana < dr) Query_ma(rc[t], dep + );
if (ana < dl) Query_ma(lc[t], dep + );
}
}
}

通常带有表示点的结构体:

 struct No {
int v[];
inline void Read() {
scanf("%d%d", &v[], &v[]);
}
inline friend bool operator < (No a, No b) {
return a.v[Mt] < b.v[Mt];
}
} p[N], qi;

(注:$qi$表示当前询问点,$Mt$表示当前分割的维度)

当然还有某些问题要求第$k$远点,只要每次查到一个点就扔到堆里去,时时维护最远的$k$个就好了,因为KD-tree剪掉了很多不必要的点,所以可以认为扔到堆里的元素并不多。

或者说动态的问题需要动态开点,开多了就可能导致树不平衡,隔一会重构就好了。

当然KD-tree在坐标系上最大的优越之处在于乱搞,旋转一下坐标系之后什么都拦不住KD-tree啦。

比如说APIO 2018的选圈圈。。。把圆用矩形框起来,每次暴力找就好了。

 #include <cstdio>
#include <algorithm> const int N = ;
const double Alpha = 1.926, EPS = 1e-; int n, Mt, ans[N]; struct No {
double v[], r; int id;
inline void Read(double x = , double y = ) {
scanf("%lf%lf%lf", &x, &y, &r);
v[] = x * cos(Alpha) + y * sin(Alpha);
v[] = y * cos(Alpha) - x * sin(Alpha);
}
inline friend bool operator < (No a, No b) {
return a.v[Mt] < b.v[Mt];
}
} pp[N], p[N], qi; inline bool cmp_r(No a, No b) {
return (a.r == b.r)? (a.id < b.id) : (a.r > b.r);
}
inline double Sqr(double x) {
return x * x;
} namespace KD {
int Rt, lc[N], rc[N];
double l[N], r[N], d[N], u[N];
inline void Merge(int x, int y) {
l[x] = std::min(l[x], l[y]);
r[x] = std::max(r[x], r[y]);
d[x] = std::min(d[x], d[y]);
u[x] = std::max(u[x], u[y]);
}
inline void Up(int t) {
l[t] = p[t].v[] - p[t].r;
r[t] = p[t].v[] + p[t].r;
d[t] = p[t].v[] - p[t].r;
u[t] = p[t].v[] + p[t].r;
if (lc[t]) Merge(t, lc[t]);
if (rc[t]) Merge(t, rc[t]);
}
int Build(int l, int r, int dep) {
if (l >= r) return (l == r)? (Up(l), l) : ();
Mt = dep & ; int md = (l + r) >> ;
std::nth_element(p + l, p + md, p + + r);
lc[md] = Build(l, md - , dep + );
rc[md] = Build(md + , r, dep + );
Up(md); return md;
}
inline int Out(int t) {
int re1 = r[t] < qi.v[] - qi.r - EPS || l[t] > qi.v[] + qi.r + EPS;
int re2 = u[t] < qi.v[] - qi.r - EPS || d[t] > qi.v[] + qi.r + EPS;
return re1 || re2;
}
inline int Check(int t) {
return Sqr(p[t].r + qi.r) + EPS >= Sqr(p[t].v[] - qi.v[]) + Sqr(p[t].v[] - qi.v[]);
}
void Query(int t) {
if (!t || Out(t)) return;
if (!ans[p[t].id] && Check(t)) ans[p[t].id] = qi.id;
if (lc[t]) Query(lc[t]);
if (rc[t]) Query(rc[t]);
}
} int main() {
scanf("%d", &n);
for (int i = ; i <= n; ++i) {
p[i].Read();
p[i].id = i;
pp[i] = p[i];
}
std::sort(pp + , pp + + n, cmp_r);
KD::Rt = KD::Build(, n, ); for (int i = ; i <= n; ++i) {
if (!ans[pp[i].id]) {
ans[pp[i].id] = pp[i].id;
qi = pp[i];
KD::Query(KD::Rt);
}
}
for (int i = ; i <= n; ++i) {
printf("%d ", ans[i]);
} return ;
}

二维线段树的替代品:

由于KD-tree本身就和值域没有什么关系,涉及到二维数点、矩形修改、矩形询问等问题可以比较方便的做,只要每个点维护一个矩形,然后大致就和线段树差不多了。

其实很多问题都能转化为二维平面甚至多维上的数点问题,有些问题离线后把时间也算成一维也是一个常用套路,KD-tree在这方面处理能力较强,适用范围较广。

要注意KD-tree上每一个点都是一个真实的点,修改时不要忘记更新它本身。

可能左右两个子节点表示的矩形存在相交,有时候自顶向下不一定好。

【科技】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的POI的封装与应用

    Java对Excel表格的导出一直是对我有种可怕噩梦的东西,每次对要建立行与列,并一个一个放值,我是从心底拒绝的. 处于项目需求,需要导出表格,于是找到网上一版很好的开发, <不想用POI?几行 ...

  2. Netty源码分析第1章(Netty启动流程)---->第1节: 服务端初始化

    Netty源码分析第一章:  Server启动流程 概述: 本章主要讲解server启动的关键步骤, 读者只需要了解server启动的大概逻辑, 知道关键的步骤在哪个类执行即可, 并不需要了解每一步的 ...

  3. Ubuntu下LimeSDR Mini使用说明

    本文内容.开发板及配件仅限用于学校或科研院所开展科研实验! 淘宝店铺名称:开源SDR实验室 LimeSDR链接:https://item.taobao.com/item.htm?spm=a230r.1 ...

  4. 云计算时代,传统企业 IT 从业者如何做好转型?

    本文来源于国外社区 DZone,作者 Dennis O'Reilly 撰写过多篇关于云计算.混合云等内容的文章,本文内容围绕云计算时代,企业纷纷上云,传统 IT 从业者如何做好转型. 本文由“数梦工场 ...

  5. XSS工具

    1.BEEF KALI中启动BEEFXSS PAYLOAD为 <script src=”http://攻击机IP:3000/hook.js”></script> 将攻击代码插入 ...

  6. maven 添加spring/springmvc依赖项

    <spring.version>4.3.18.RELEASE</spring.version> <dependencies> <!--添加spring.spr ...

  7. Trait 是什么东西

    PHP官方手册里面写的内容是 自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait. Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制.Trait 为了减少 ...

  8. 第一章 HTML介绍

    1.1 Html和CSS的关系 学习web前端开发基础技术需要掌握:HTML.CSS.JavaScript语言.下面我们就来了解下这三门技术都是用来实现什么的: 1. HTML是网页内容的载体.内容就 ...

  9. 面向对象OO第1-3次作业总结

    面向对象OO第1-3次作业总结 学习OO已经四周了,对OO以及JAVA的编程也算终于了解了一丢丢.现在做完了三次的编程作业,对前三次的作业做一次总结. 第一次作业 ------------------ ...

  10. binding(转)

    1,Data Binding在WPF中的地位 程序的本质是数据+算法.数据会在存储.逻辑和界面三层之间流通,所以站在数据的角度上来看,这三层都很重要.但算法在3层中的分布是不均匀的,对于一个3层结构的 ...