用途

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

思想

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

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

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

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

具体地,给每一层一个\(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. RabbitMQ集群部署、高可用和持久化

    RabbitMQ 安装和使用 1.安装依赖环境 在 http://www.rabbitmq.com/which-erlang.html 页面查看安装rabbitmq需要安装erlang对应的版本 在 ...

  2. C#插入时间

    //获取日期+时间 DateTime.Now.ToString(); // 2008-9-4 20:02:10 DateTime.Now.ToLocalTime().ToString(); // 20 ...

  3. redis设置密码,解决重启后密码丢失及自启服务配置

    一.安装redis redis3.0及redisManage管理工具 链接:https://pan.baidu.com/s/1p5EWeF2Jgsw9xOE1ADMmRg 提取码:thyf 二.red ...

  4. ABP 基于DDD的.NET开发框架 学习(一)

    ABP总体介绍 ABP是ASP.NET Boilerplate Project,ASP.NET样板项目. ABP框架定位于快速开发 ABP是一个用于最快实践和流行开发现代Web应用程序的新起点,旨在成 ...

  5. Go part 7 反射,反射类型对象,反射值对象

    反射 反射是指在程序运行期间对程序本身进行访问和修改的能力,(程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分,在运行程序时,程序无法获取自身的信息) 支持反射的语言可以在程序编 ...

  6. jupyter修改默认目录

    有趣的事,Python永远不会缺席! 如需转发,请注明出处:小婷儿的python https://www.cnblogs.com/xxtalhr/p/10841241.html 一.修改 win10 ...

  7. CentOS 7中调整默认开启终端数量

    #vim /etc/securetty #然后将不需要使用到的进行注释掉就可以了 就这么简单,在强大的百度上找了半天愣是没找到

  8. vue框架之脚手架(vue-cli)的使用

    前期准备 1.在使用之前需要安装node.js,https://nodejs.org/dist/latest-v8.x/ 2.下载之后在cmd中测试 node -v npm -v 如图上即可 3.下载 ...

  9. Python函数式编程-map/reduce

    1.map map()传入的第一个参数是f,即函数对象本身. map()函数接收两个参数,一个是函数,一个是Interable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterat ...

  10. Gym - 102012H Rikka with A Long Colour Palette N线段K色贪心染色

    给你数轴上的N条线段和K种颜色 K和N1e5 要你把这N条线段染色 使得有K种不同颜色的线段长度最长 首先很容易想到被至少K段线段覆盖的区间是一定有贡献的 接下来就是怎么染色的问题 我们把这N个区间的 ...