用途

  • 做各种二维三维四维偏序等等。
  • 代替空间巨大的树套树。
  • 数据较弱的时候水分。

思想

我们发现平衡树这种东西功能强大,然而只能做一维上的询问修改,显得美中不足。

于是我们尝试用平衡树的这种二叉树结构,做更高维的事情。

继续沿用平衡树的左儿子比自己小、右儿子比自己大的形态。这时发现,如果小于号定义得不好,那么做高维询问的时候就很难做。

发明者想到了这样一个方法:我们每过一层就划分一次超矩形。

具体地,给每一层一个\(type\),表示这一层是按哪一维切割。切割某一维的时候,拿出中位数,然后分成两边,于是就完成了小于号的定义。

给一张图:

查询的时候,类似线段树,只要完全包含了就返回,否则无脑往左右儿子跑。

如果要动态加入点怎么办?使用替罪羊树的思想,如果太过不平衡就拍扁重构。

时间复杂度分析?建树显然是\(O(n\log n)\)的,插入据说是\(O(n\log^2 n)\),而询问……引用一下AKMer的证明:

实现

以下代码基本没有参考别人的代码,如果有错误请指正。

为了方便,以下均用二维举例子,拓展到高维不会太难。

(其实也是因为我只写过二维)

定义

定义就是最简单的定义。

struct Point{int x,y,w;}p[sz];
int D;
inline bool cmp(const Point &x,const Point &y){return D?x.y<y.y:x.x<y.x;}
const db alpha=0.75;
int ch[sz<<3][2],fa[sz<<3],size[sz<<3],sum[sz<<3],type[sz<<3]; // 数组大小我也不知道要开几倍,反正O(n)的多开一点应该也没事
Point P[sz<<3];
int L[sz<<3][2],R[sz<<3][2]; // 每一维的上下限
#define ls ch[x][0]
#define rs ch[x][1]
int root,cnt;
int st[sz],top;
void erase(int x){st[++top]=x;ls=rs=size[x]=sum[x]=L[x][0]=R[x][0]=L[x][1]=R[x][1]=0;P[x]=(Point){0,0,0};}
int newnode(){return top?st[top--]:++cnt;} // k-d tree经常重构,所以最好垃圾回收

拍扁重构

利用STL中的nth_element函数,可以做到\(O(n\log n)\)建树。

为了切割得较为均匀,这里选择每一维轮换切割。还有另一种方法是切方差最大的一维,但是懒得写了。

Point pp[sz];int m;
int build(int l,int r,int f)
{
if (l>r) return 0;
int x=newnode();fa[x]=f;
type[x]=type[f]^1;
int mid=(l+r)>>1;
D=type[x];nth_element(pp+l,pp+mid,pp+r+1,cmp);
P[x]=pp[mid];
L[x][0]=R[x][0]=P[x].x,L[x][1]=R[x][1]=P[x].y;
ls=build(l,mid-1,x),rs=build(mid+1,r,x);
size[x]=size[ls]+size[rs]+1;sum[x]=sum[ls]+sum[rs]+P[x].w;
rep(i,0,1) if (ch[x][i]) rep(k,0,1) chkmin(L[x][k],L[ch[x][i]][k]),chkmax(R[x][k],R[ch[x][i]][k]);
return x;
}

插入

插入时要判是否平衡,如果不平衡就擦除一整棵子树并重构。

void Erase(int x){if (!x) return;pp[++m]=P[x];Erase(ls),Erase(rs);erase(x);}
void insert(Point p)
{
int top=-1,x=root;
if (!x) return (void)(pp[1]=p,root=build(1,1,0));
while (233)
{
if (max(size[ls],size[rs])>size[x]*alpha&&top==-1) top=x;
++size[x];sum[x]+=p.w;
chkmin(L[x][0],p.x),chkmax(R[x][0],p.x);
chkmin(L[x][1],p.y),chkmax(R[x][1],p.y);
D=type[x];int &y=ch[x][!cmp(p,P[x])];
if (!y)
{
y=newnode();
L[y][0]=R[y][0]=p.x;
L[y][1]=R[y][1]=p.y;
size[y]=1;sum[y]=p.w;type[y]=type[x]^1;fa[y]=x;
P[y]=p;
break;
}
x=y;
}
if (top==-1) return;
m=0;
if (top==root) { Erase(top); root=build(1,m,0); return; }
int f=fa[top],&t=ch[f][ch[f][1]==top];
Erase(top);
t=build(1,m,f);
}

询问

无脑做就好了。

int query(int x,int l0,int r0,int l1,int r1)
{
if (!x) return 0;
if (l0<=L[x][0]&&R[x][0]<=r0&&l1<=L[x][1]&&R[x][1]<=r1) return sum[x];
if (r0<L[x][0]||R[x][0]<l0||r1<L[x][1]||R[x][1]<l1) return 0;
return query(ls,l0,r0,l1,r1)+query(rs,l0,r0,l1,r1)+(l0<=P[x].x&&P[x].x<=r0&&l1<=P[x].y&&P[x].y<=r1?P[x].w:0);
}

例题

LOJ112 三维偏序

LOJ3159「NOI2019」弹跳

目前我也只知道这两个了,毕竟我自己也是刚学qwq

K-D Tree学习笔记的更多相关文章

  1. 珂朵莉树(Chtholly Tree)学习笔记

    珂朵莉树(Chtholly Tree)学习笔记 珂朵莉树原理 其原理在于运用一颗树(set,treap,splay......)其中要求所有元素有序,并且支持基本的操作(删除,添加,查找......) ...

  2. dsu on tree学习笔记

    前言 一次模拟赛的\(T3\):传送门 只会\(O(n^2)\)的我就\(gg\)了,并且对于题解提供的\(\text{dsu on tree}\)的做法一脸懵逼. 看网上的其他大佬写的笔记,我自己画 ...

  3. Link Cut Tree学习笔记

    从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...

  4. 矩阵树定理(Matrix Tree)学习笔记

    如果不谈证明,稍微有点线代基础的人都可以在两分钟内学完所有相关内容.. 行列式随便找本线代书看一下基本性质就好了. 学习资源: https://www.cnblogs.com/candy99/p/64 ...

  5. k-d tree 学习笔记

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

  6. splay tree 学习笔记

    首先感谢litble的精彩讲解,原文博客: litble的小天地 在学完二叉平衡树后,发现这是只是一个不稳定的垃圾玩意,真正实用的应有Treap.AVL.Splay这样的查找树.于是最近刚学了学了点S ...

  7. dsu on tree 学习笔记

    这是一个黑科技,考虑树链剖分后,每个点只会在轻重链之间转化\(log\)次. 考虑暴力是怎么写的,每次枚举一个点,再暴力把子树全部扫一边. \(dsu\ on\ tree.\)的思想就是保留重儿子不清 ...

  8. kd tree学习笔记 (最近邻域查询)

    https://zhuanlan.zhihu.com/p/22557068 http://blog.csdn.net/zhjchengfeng5/article/details/7855241 KD树 ...

  9. bzoj 1598: [Usaco2008 Mar]牛跑步 [k短路 A*] [学习笔记]

    1598: [Usaco2008 Mar]牛跑步 题意:k短路 ~~貌似A*的题目除了x数码就是k短路~~ \[ f(x) = g(x) + h(x) \] \(g(x)\)为到达当前状态实际代价,\ ...

随机推荐

  1. 一行代码实现Vue微信支付,无需引用wexin-sdk库,前后端分离HTML微信支付,无需引用任何库

    前后端分离项目实现微信支付的流程: 1:用户点击支付 2:请求服务端获取支付参数 3:客户端通过JS调起微信支付(微信打开的网页) * 本文主要解决的是第3步,视为前两步已经完成,能正确拿到支付参数, ...

  2. 针对Web的攻击技术

    主动攻击 SQL注入攻击 OS命令注入攻击 会话劫持 被动攻击 XSS攻击 CSRF攻击 HTTP首部注入攻击 会话固定攻击 一.主动攻击 1.SQL注入攻击(案例) 什么是SQL? SQL是用来操作 ...

  3. mongodb入门基本语法

    show dbs 查看所有数据库列表 二. 创建数据库 使用数据库. 创建数据库 use student 如果真的想把这个数据库创建成功, 那么必须插入一个数据. 数据库中不能直接插入数据,只能往集合 ...

  4. 图片上传怎么用File接受文件

    xl_echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.——这才是真正的堪称强大!! - ...

  5. 关于iview下拉菜单无法添加点击事件的解决办法

    效果如下图所示,点击下拉菜单,点击退出,然后跳到登录界面 代码如下: <Dropdown trigger="click" style="margin-left: 2 ...

  6. 理解JVM之类加载机制

    类完整的生命周期包括加载,验证,准备,解析,初始化,使用,卸载,七个阶段.其中验证,准备,解析统称为连接,类的卸载在前面的关于垃圾回收的博文中已经介绍. 加载,验证,准备,初始化,卸载这五个阶段的顺序 ...

  7. Hystrix原理与实战

    Hystrix原理与实战 背景 分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务. 比如:订单服务调用商品服务,商品服务调用库存服务. 对于同步调用,当库存服务不可用时,商品 ...

  8. centos7创建共享文件夹

    0.检查是否已经安装samba rpm -qi samba 1.未安装,安装samba, 如果已安装,请忽略: yum -y install samba samba-client 2.共享一个目录,使 ...

  9. Android面试题 请解释下单线程模型中Message、Handler、MessageQueue、Looper之间的关系

    简单的说,Handler获取当前线程中的looper对象,looper用来存放从MessageQueue中取出的Message,再由Handler进行Message分发和处理,按照先进先出执行. Me ...

  10. Python绘制混淆矩阵,汉字显示label

    1. 在计算出混淆矩阵之后,想自己绘制图形(并且在图形上显示汉字),可用 #coding=utf-8 import matplotlib.pyplot as plt import numpy as n ...