K-D Tree学习笔记
用途
- 做各种二维三维四维偏序等等。
- 代替空间巨大的树套树。
- 数据较弱的时候水分。
思想
我们发现平衡树这种东西功能强大,然而只能做一维上的询问修改,显得美中不足。
于是我们尝试用平衡树的这种二叉树结构,做更高维的事情。
继续沿用平衡树的左儿子比自己小、右儿子比自己大的形态。这时发现,如果小于号定义得不好,那么做高维询问的时候就很难做。
发明者想到了这样一个方法:我们每过一层就划分一次超矩形。
具体地,给每一层一个\(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学习笔记的更多相关文章
- 珂朵莉树(Chtholly Tree)学习笔记
珂朵莉树(Chtholly Tree)学习笔记 珂朵莉树原理 其原理在于运用一颗树(set,treap,splay......)其中要求所有元素有序,并且支持基本的操作(删除,添加,查找......) ...
- dsu on tree学习笔记
前言 一次模拟赛的\(T3\):传送门 只会\(O(n^2)\)的我就\(gg\)了,并且对于题解提供的\(\text{dsu on tree}\)的做法一脸懵逼. 看网上的其他大佬写的笔记,我自己画 ...
- Link Cut Tree学习笔记
从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...
- 矩阵树定理(Matrix Tree)学习笔记
如果不谈证明,稍微有点线代基础的人都可以在两分钟内学完所有相关内容.. 行列式随便找本线代书看一下基本性质就好了. 学习资源: https://www.cnblogs.com/candy99/p/64 ...
- k-d tree 学习笔记
以下是一些奇怪的链接有兴趣的可以看看: https://blog.sengxian.com/algorithms/k-dimensional-tree http://zgjkt.blog.uoj.ac ...
- splay tree 学习笔记
首先感谢litble的精彩讲解,原文博客: litble的小天地 在学完二叉平衡树后,发现这是只是一个不稳定的垃圾玩意,真正实用的应有Treap.AVL.Splay这样的查找树.于是最近刚学了学了点S ...
- dsu on tree 学习笔记
这是一个黑科技,考虑树链剖分后,每个点只会在轻重链之间转化\(log\)次. 考虑暴力是怎么写的,每次枚举一个点,再暴力把子树全部扫一边. \(dsu\ on\ tree.\)的思想就是保留重儿子不清 ...
- kd tree学习笔记 (最近邻域查询)
https://zhuanlan.zhihu.com/p/22557068 http://blog.csdn.net/zhjchengfeng5/article/details/7855241 KD树 ...
- bzoj 1598: [Usaco2008 Mar]牛跑步 [k短路 A*] [学习笔记]
1598: [Usaco2008 Mar]牛跑步 题意:k短路 ~~貌似A*的题目除了x数码就是k短路~~ \[ f(x) = g(x) + h(x) \] \(g(x)\)为到达当前状态实际代价,\ ...
随机推荐
- service mc_start.sh does not support chkconfig
在构建docker镜像时,编写Dockerfile构建镜像时,配置自启动脚本报错,service mc_start.sh does not support chkconfig 添加下面两句到 #!/b ...
- 怎样解决Script error报错问题
如果脚本网址与网页网址不在同一个域(比如使用了 CDN), 那如果这个脚本执行报错了, 就会报:Script error. 由于同源策略, 浏览器禁止向外部脚本泄漏信息, 因此不会提供完整的报错信息, ...
- .ajaxStart() / .ajaxStop() —— ajax请求开始时 / 结束时触发
一..ajaxStart()——ajax请求开始时触发 描述:ajax请求开始时触发 .ajaxStart()的回调函数,全局的,所有的ajax都可以用 写法:元素.ajaxStart(functi ...
- 常用NLog配置
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSe ...
- FTP搭建注意事项
正常的FTP搭建步骤很简单,随便网搜一篇文章就出来了 下面提出一个网址可供学习 https://blog.csdn.net/m0_38044299/article/details/81627607 但 ...
- 在Windows平台搭建C语言开发环境
一.在Windows平台搭建DEV C++集成开发环境 官网 https://sourceforge.net/projects/orwelldevcpp/ 中下载Dev C++运行即可 环境准 ...
- UICollectionViewLayout详解,文档翻译
实现一个UICollectionView,和UITableView类似,不过初始化的时候要传入一个UICollectionViewLayout. 苹果给UIcollectionview中的所有视图都来 ...
- jar找不到问题解决
1.File->Settings->搜maven->看Local repository的路径配置是否正确,再看User settings file路径配置是否正确,再看xml内容配置 ...
- Linux常用命令【1】
打包和压缩文件 : cd /home 进入 '/ home' 目录' cd .. 返回上一级目录 cd ../.. 返回上两级目录 cd 进入个人的主目录 cd ~user1 进入个人的主目录 cd ...
- STL的sort函数是使用什么排序算法的?
先占坑,大概就是主要快速排序+插入排序+堆排序的合体