吐槽
kd-tree这个东西很早就听说过了但是qwq一直没有去了解
(原因的话。。啊哈哈听说是什么跟二维平面之类的东西有关的所以就怂掉了qwq没错就是怂qwq)
但其实好像。。真的很暴力啊qwq知道思路之后随便乱搞系列qwq
时间复杂度什么的应该是玄学恩qwq(网上看到有dalao说期望复杂度是O(n^(d-1)/d),不会证就是这样qwq)
正题
首先kd-tree是一棵类似二叉查找树的东西
拿二维的kd-tree为例,每个数据有两个关键值\(x\)和\(y\)
那么kd-tree的总的思路就是,每层根据一个关键值(记为\(val\)好了)来排序,这两个关键值轮着来(其实就是。。第一层按照\(x\)排序,第二层\(y\),然后第三层又是\(x\),以此类推)
kd-tree满足的性质是,\(x\)左子树中所有节点的\(val\)小于\(x\)的\(val\),右子树中所有节点的\(val\)大于\(x\)的\(val\)
比如说我们现在要构建\(n\)组数据的kd-tree,那么方法很简单
首先stl有一个十分吼的东西叫做nth_element(),具体用法是将\([l,r]\)这个区间内的第\(k\)个数放到这段区间中的第\(k\)个位置,然后前面的数小于它,后面的数大于它,但是不保证有序
调用:nth_element(a+l,a+mid,a+r+1,cmp)
如果说没有修改操作(bzoj2850),那么就是递归处理,build(),每次把区间的中间那个数拎出来,用nth_element处理一下,然后左边的就是mid的左子树,右边的就是右子树,然后递归处理
(所以说就是怎么乱搞都ok啦ovo)
那么这个东西有啥用呢?其实就是相当于排了个序,我们对于每个节点多维护4个值,分别是\(i\)子树下\(x\)的最大最小值和\(y\)的最大最小值,然后再维护其他的东西(什么sum啊之类的具体题目具体分析)
这样如果说我要查某个范围,我就能快速判断出这个子树是否需要遍历,如果说没有交集直接跳过,如果完全包含那直接把整个子树的信息拿来更新答案之类的,如果部分包含那就没办法了老老实实遍历咯
写起来还是十分简洁的ovo
如果说有修改操作(bzoj4066),那么就不能预处理出整棵kd-tree了,要一个一个点insert之类的
insert的话其实也很简单,就是按照前面说的排序方式每次判断小于的话就往左子树走反之右子树,找到应该在的位置把信息存进去就好了
但是这就涉及到一个问题,这样添加的话是可能退化成一条链的。。那就很gg了
所以我们需要不定时重构(有没有想到替罪羊啊哈哈哈。。好吧其实重构的方式确实有点像恩qwq)
其实就是把当前得到的所有值跑一遍无修改操作的build函数就好了
这样就可以保证变成一条链的样子不会持续太久。。那么复杂度就会优秀很多了(然而还是玄学qwq)
可以用来处理一些强制在线的区间修改查询,甚至可以用来处理一些cdq分治的题目(hdu5126)
大概就是这样咯ovo