K-D tree入门
久仰K-D tree大名已久,终于在合适的时候遇见了合适的水题入了坑入了门
K-D tree是什么##
K-D tree是什么? 按名字上翻译来就是K维的树,就是一个用来维护K维空间的点的平衡二叉树
K-D tree有什么用##
K-D tree可以进行空间上的操作,最经典的就是查询最近/最远 点对
还有很多我不知道
K-D tree的原理与实现##
K-D tree,又有一个名字叫做划分树,与其原理相联系
类似于普通的平衡树,对于普通的平衡树的节点u,其左右子树分别是权值小于u的和权值大于u的,该节点就相当于在一个值域上在u的值处进行了分割
而kd-tree对于多维空间进行分割,一个节点储存着以下信息:
struct node{
int d[K],s[2],x[2],y[2],......;
}e[maxn];
d就是该节点储存的点的K维坐标
s储存着其左右儿子
剩余的若干数组储存着以该节点为根的子树的各维度的最值
也就是说,一棵子树实际上对应着一个空间区域,而根节点将该空间区域划分为左右两部分
而该空间的信息就储存在子树的根节点中
但是这是在一个多维空间,将空间区域划分有多种方式
一般地,kd-tree垂直于其中一个坐标轴将平面划分开
以2维为例,如下图所示

如图圆圈表示节点,将区域进行划分
一般遵循以下规律构造:
①各层节点交替划分各维空间
根节点划分x坐标
其儿子划分y坐标
其孙子划分z坐标
......
②每一层的区域中,按该层划分的坐标排序,选取其中位数作为划分点进行划分
切点作为父节点,左边的点划分到左子树中,右边的点划分到右子树中
③逐层划分,直至划分区域无节点
在C++的STL中,有一个函数nth_element()可以在\(O(n)\)时间内将一个数组第k大找出,并将小于的放在左边,大于的放在右边
具体实现类似快排
对于K维空间,建树复杂度\(O(Knlogn)\)
二维建树代码如下:
#define ls e[u].s[0]
#define rs e[u].s[1]
#define cmin(x,y) (x > y ? x = y : x)
#define cmax(x,y) (x < y ? x = y : x)
struct point{int d[2];}a[maxn];
struct node{int d[2],s[2],x[2],y[2];}e[maxn];
int n,rt,D,x,y;
bool operator <(const point& a,const point& b){
return a.d[D] == b.d[D] ? a.d[D ^ 1] < b.d[D ^ 1] : a.d[D] < b.d[D];
}
void pup(int u){
if (ls){
cmin(e[u].x[0],e[ls].x[0]); cmax(e[u].x[1],e[ls].x[1]);
cmin(e[u].y[0],e[ls].y[0]); cmax(e[u].y[1],e[ls].y[1]);
}
if (rs){
cmin(e[u].x[0],e[rs].x[0]); cmax(e[u].x[1],e[rs].x[1]);
cmin(e[u].y[0],e[rs].y[0]); cmax(e[u].y[1],e[rs].y[1]);
}
}
int build(int l,int r,int d){
D = d; int u = l + r >> 1;
nth_element(a + l,a + u,a + r + 1);
e[u].d[0] = e[u].x[0] = e[u].x[1] = a[u].d[0];
e[u].d[1] = e[u].y[0] = e[u].y[1] = a[u].d[1];
if (l < u) ls = build(l,u - 1,d ^ 1);
if (r > u) rs = build(u + 1,r,d ^ 1);
pup(u);
return u;
}
查询最近/远点对##
以最近为例
与普通的暴力不同,在KDtree中查询最近点对,预期复杂度为\(O(logn)\),可以被卡为\(O(\sqrt{N})\)
我们到一个节点时,用该节点更新答案,并计算左右子树的估价函数
由于每棵子树都对应一个区域,可以由此计算出每棵子树的最近值
如果最近的点的贡献都比当前答案大,那么就不用访问该子树了
以2维为例,估价函数可以这样写:
#define getd(u) (max(x - e[u].x[1],0) + max(e[u].x[0] - x,0) + max(y - e[u].y[1],0) + max(e[u].y[0] - y,0))
#define getdx(u) (max(abs(e[u].x[0] - x),abs(e[u].x[1] - x)) + max(abs(e[u].y[0] - y),abs(e[u].y[1] - y)))
实际上就是求与四个顶点距离的最值
由此可以写出搜索函数:
void qmx(int u){
LL t = equal(u) ? -INF : (abs(e[u].d[0] - x) + abs(e[u].d[1] - y)),d[2];
if (ls) d[0] = getdx(ls); else d[0] = -INF;
if (rs) d[1] = getdx(rs); else d[1] = -INF;
cmax(mx,t); t = d[0] <= d[1];
if (d[t] > mx) qmx(e[u].s[t]); t ^= 1;
if (d[t] > mx) qmx(e[u].s[t]);
}
void qmn(int u){
int t = equal(u) ? INF : (abs(e[u].d[0] - x) + abs(e[u].d[1] - y)),d[2];
if (ls) d[0] = getd(ls); else d[0] = INF;
if (rs) d[1] = getd(rs); else d[1] = INF;
cmin(mn,t); t = d[0] >= d[1];
if (d[t] < mn) qmn(e[u].s[t]); t ^= 1;
if (d[t] < mn) qmn(e[u].s[t]);
}
插入##
类似平衡树插入
void insert(int u,int d){
if (e[u].d[d] < dd[d]){
if (ls) insert(ls,d ^ 1);
else {
ls = ++n;
e[ls].d[0] = e[ls].x[0] = e[ls].x[1] = dd[0];
e[ls].d[1] = e[ls].y[0] = e[ls].y[1] = dd[1];
}
}
else {
if (rs) insert(rs,d ^ 1);
else {
rs = ++n;
e[rs].d[0] = e[rs].x[0] = e[rs].x[1] = dd[0];
e[rs].d[1] = e[rs].y[0] = e[rs].y[1] = dd[1];
}
}
pup(u);
}
例题##
由以上基础,我们就可以轻松A掉SDOI2010 hideseek了
用每个点搜一次就好
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
#define ls e[u].s[0]
#define rs e[u].s[1]
#define cmin(x,y) (x > y ? x = y : x)
#define cmax(x,y) (x < y ? x = y : x)
#define getd(u) (max(x - e[u].x[1],0) + max(e[u].x[0] - x,0) + max(y - e[u].y[1],0) + max(e[u].y[0] - y,0))
#define getdx(u) (max(abs(e[u].x[0] - x),abs(e[u].x[1] - x)) + max(abs(e[u].y[0] - y),abs(e[u].y[1] - y)))
#define equal(u) (e[u].d[0] == x && e[u].d[1] == y)
using namespace std;
const int maxn = 100005,maxm = 100005,INF = 2100000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
struct point{int d[2];}a[maxn];
struct node{int d[2],s[2],x[2],y[2];}e[maxn];
int n,rt,D,x,y; LL mx,mn;
bool operator <(const point& a,const point& b){
return a.d[D] == b.d[D] ? a.d[D ^ 1] < b.d[D ^ 1] : a.d[D] < b.d[D];
}
void pup(int u){
if (ls){
cmin(e[u].x[0],e[ls].x[0]); cmax(e[u].x[1],e[ls].x[1]);
cmin(e[u].y[0],e[ls].y[0]); cmax(e[u].y[1],e[ls].y[1]);
}
if (rs){
cmin(e[u].x[0],e[rs].x[0]); cmax(e[u].x[1],e[rs].x[1]);
cmin(e[u].y[0],e[rs].y[0]); cmax(e[u].y[1],e[rs].y[1]);
}
}
int build(int l,int r,int d){
D = d; int u = l + r >> 1;
nth_element(a + l,a + u,a + r + 1);
e[u].d[0] = e[u].x[0] = e[u].x[1] = a[u].d[0];
e[u].d[1] = e[u].y[0] = e[u].y[1] = a[u].d[1];
if (l < u) ls = build(l,u - 1,d ^ 1);
if (r > u) rs = build(u + 1,r,d ^ 1);
pup(u);
return u;
}
void qmx(int u){
LL t = equal(u) ? -INF : (abs(e[u].d[0] - x) + abs(e[u].d[1] - y)),d[2];
if (ls) d[0] = getdx(ls); else d[0] = -INF;
if (rs) d[1] = getdx(rs); else d[1] = -INF;
cmax(mx,t); t = d[0] <= d[1];
if (d[t] > mx) qmx(e[u].s[t]); t ^= 1;
if (d[t] > mx) qmx(e[u].s[t]);
}
void qmn(int u){
int t = equal(u) ? INF : (abs(e[u].d[0] - x) + abs(e[u].d[1] - y)),d[2];
if (ls) d[0] = getd(ls); else d[0] = INF;
if (rs) d[1] = getd(rs); else d[1] = INF;
cmin(mn,t); t = d[0] >= d[1];
if (d[t] < mn) qmn(e[u].s[t]); t ^= 1;
if (d[t] < mn) qmn(e[u].s[t]);
}
int main(){
n = read();
for (int i = 1; i <= n; i++) a[i].d[0] = read(),a[i].d[1] = read();
rt = build(1,n,0);
LL ans = INF;
for (int i = 1; i <= n; i++){
x = a[i].d[0]; y = a[i].d[1];
mx = 0; qmx(rt);
mn = INF; qmn(rt);
ans = min(ans,mx - mn);
}
printf("%lld\n",ans);
return 0;
}
BZOJ2648 / BZOJ2716 两道比较卡常数
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define max(a,b) (a > b ? a : b)
#define min(a,b) (a < b ? a : b)
#define cmax(a,b) (a < b ? a = b : a)
#define cmin(a,b) (a > b ? a = b : a)
#define ls e[u].s[0]
#define rs e[u].s[1]
using namespace std;
const int maxn = 1000005,maxm = 500005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
struct node{
int d[2],s[2],x[2],y[2];
}e[maxn];
int m,n,x,y,D,dd[2],ans,rt;
struct point{int d[2];}p[maxm];
inline bool cmp(const point& a,const point& b){
return a.d[D] < b.d[D];
}
int getd(int u){
int tmp = 0;
tmp += max(e[u].x[0] - x,0);
tmp += max(x - e[u].x[1],0);
tmp += max(e[u].y[0] - y,0);
tmp += max(y - e[u].y[1],0);
return tmp;
}
inline void pup(int u){
if (ls){
cmin(e[u].x[0],e[ls].x[0]); cmax(e[u].x[1],e[ls].x[1]);
cmin(e[u].y[0],e[ls].y[0]); cmax(e[u].y[1],e[ls].y[1]);
}
if (rs){
cmin(e[u].x[0],e[rs].x[0]); cmax(e[u].x[1],e[rs].x[1]);
cmin(e[u].y[0],e[rs].y[0]); cmax(e[u].y[1],e[rs].y[1]);
}
}
int build(int l,int r,int d){
D = d; int u = l + r >> 1;
nth_element(p + l,p + u,p + r + 1,cmp);
e[u].d[0] = e[u].x[0] = e[u].x[1] = p[u].d[0];
e[u].d[1] = e[u].y[0] = e[u].y[1] = p[u].d[1];
if (l < u) ls = build(l,u - 1,d ^ 1);
if (r > u) rs = build(u + 1,r,d ^ 1);
pup(u);
return u;
}
void insert(int u,int d){
if (e[u].d[d] < dd[d]){
if (ls) insert(ls,d ^ 1);
else {
ls = ++n;
e[ls].d[0] = e[ls].x[0] = e[ls].x[1] = dd[0];
e[ls].d[1] = e[ls].y[0] = e[ls].y[1] = dd[1];
}
}
else {
if (rs) insert(rs,d ^ 1);
else {
rs = ++n;
e[rs].d[0] = e[rs].x[0] = e[rs].x[1] = dd[0];
e[rs].d[1] = e[rs].y[0] = e[rs].y[1] = dd[1];
}
}
pup(u);
}
void query(int u){
int t = abs(e[u].d[0] - x) + abs(e[u].d[1] - y),d[2];
if (ls) d[0] = getd(ls); else d[0] = INF;
if (rs) d[1] = getd(rs); else d[1] = INF;
cmin(ans,t); t = d[0] >= d[1];
if (d[t] < ans) query(e[u].s[t]); t ^= 1;
if (d[t] < ans) query(e[u].s[t]);
}
int main(){
n = read(); m = read();
for (int i = 1; i <= n; i++){
p[i].d[0] = read();
p[i].d[1] = read();
}
rt = build(1,n,0);
int t;
for (int i = 1; i <= m; i++){
t = read(); x = read(); y = read();
if (t & 1){
dd[0] = x; dd[1] = y;
insert(rt,0);
}
else {
ans = INF;
query(rt);
printf("%d\n",ans);
}
}
return 0;
}
K-D tree入门的更多相关文章
- bzoj2049 [Sdoi2008]Cave 洞穴勘测 link cut tree入门
link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isro ...
- link cut tree 入门
鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构. link cut tree:研究popoqqq那个神ppt. bzoj1036:维护access操作就可以了. ...
- dsu on tree入门
先瞎扯几句 说起来我跟这个算法好像还有很深的渊源呢qwq.当时在学业水平考试的考场上,题目都做完了不会做,于是开始xjb出题.突然我想到这么一个题 看起来好像很可做的样子,然而直到考试完我都只想出来一 ...
- POJ 3468 A Simple Problem with Integers (splay tree入门)
A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 47944 ...
- [HNOI2002]营业额统计 Splay tree入门题
题目连接:http://www.lydsy.com/JudgeOnline/problem.php?id=1588 1588: [HNOI2002]营业额统计 Time Limit: 5 Sec ...
- poj3481(splay tree 入门题)
平衡树都能做. // // main.cpp // splay // // Created by 陈加寿 on 16/3/25. // Copyright © 2016年 chenhuan001. A ...
- 手撸红黑树-Red-Black Tree 入门
一.学习红黑树前的准备: 熟悉基础数据结构 了解二叉树概念 二.红黑树的规则和规则分析: 根节点是黑色的 所有叶子节点(Null)是黑色的,一般会认定节点下空节点全部为黑色 如果节点为红色,那么子节点 ...
- dsu on tree 入门
Dus on tree 树上并查集?. 啊这,并不是的啦,他利用了树上启发式合并的思想. 他主要解决不带修改且主要询问子树信息的树上问题. 先来看到例题,CF600E . 这不就是树上莫队的经典题吗? ...
- 第46届ICPC澳门站 K - Link-Cut Tree // 贪心 + 并查集 + DFS
原题链接:K-Link-Cut Tree_第46屆ICPC 東亞洲區域賽(澳門)(正式賽) (nowcoder.com) 题意: 要求一个边权值总和最小的环,并从小到大输出边权值(2的次幂):若不存在 ...
随机推荐
- lsattr
-a 将隐藏文件的属性也显示出来 -R 连同子目录的数据也一并列出来 chattr +aij 文件名
- NBUT 1115 Cirno's Trick (水)
题意: 给出多个double数,去掉其最小的和最大的,再对余下的求均值. 思路: 再输入时将最大和最小去掉,顺便统计非最值的和,输出时除一下个数即可. #include <bits/stdc++ ...
- UVA1665 Islands (并查集)
补题,逆序考虑每个询问的时间,这样每次就变成出现新岛屿,然后用并查集合并统计.fa = -1表示没出现. 以前写过,但是几乎忘了,而且以前写得好丑的,虽然常数比较小,现在重新写练练手.每个单词后面都要 ...
- uva12264 Risk
最小值最大,就二分判断. map[i] = '0'+map[i];这样更方便 每个点拆成i,i’, S连i,cap为a[i],i’连T,cap为1(保证至少剩一个)或mid. i,i’ ,a[i] ...
- CPP-基础:关于内存分配
1:c中的malloc和c++中的new有什么区别 (1)new.delete 是操作符,可以重载,只能在C++中使用.(2)malloc.free是函数,可以覆盖,C.C++中都可以使用.(3)ne ...
- 第三届上海市大学生网络安全大赛wp&学习
wp 0x00 p200 先分析了程序关键的数据结构 分析程序逻辑,在free堆块的时候没有清空指针,造成悬挂指针,并且程序中给了system('/bin/sh'),可以利用uaf 脚本如下: 1.先 ...
- 理解GloVe模型(Global vectors for word representation)
理解GloVe模型 概述 模型目标:进行词的向量化表示,使得向量之间尽可能多地蕴含语义和语法的信息.输入:语料库输出:词向量方法概述:首先基于语料库构建词的共现矩阵,然后基于共现矩阵和GloVe模型学 ...
- java在线聊天项目 使用SWT快速制作登录窗口,可视化窗口Design 更换窗口默认皮肤(切换Swing自带的几种皮肤如矩形带圆角)
SWT成功激活后 new一个JDialog 调整到Design视图 默认的视图模式是BorderLayout,无论你怎么拖拽,只能放到东西南北中的位置上 所以,我们把视图模式调整为AbsoluteLa ...
- Codeforces Round #510 #A
http://codeforces.com/contest/1042/problem/A 题目大意就是: 现在公园里有n个长椅(要多长有多长),第i个长椅上有a[i]个人(泰山崩于前而不乱),现在又有 ...
- ubuntu命令行卸载并清理软件
卸载软件,可以使用下面这两种方式之一: sudo apt-get remove --purge [software name] sudo apt-get autoremove --purge [sof ...