kd树学习小结 -ag凯发k8国际
几个月后的upd:
学习完下面之后,实战中的总结:
0.比赛中正解就是kdtree的题目很少很少
1.几类优先考虑kdtree的题目:
k(维度) >= 3 的题目
二维平面上涉及区间标记的题目
2. 二维平面上的题目, n >= 5w 谨慎使用kdtree
非强制在线考虑cdq套数据结构(不涉及标记的话基本考虑树状数组)
强制在线考虑树状数组套主席树(该方法需要提前确定空间够不够o(nlogn2))
几种方法的常数比较:cdq套树 < 树套树 < kdtree
编程复杂度也是cdq最小,后面两个差不多
3.非写kdtree不可的题目,常数上的几个优化
<0>快速读入,快速输出的话,不超过50w的个数基本没必要
<1>重构kdtree的参数选择,插入多查询少的情况,最优参数是接近 0.5 x / (x y) 的
x是插入个数,y是查询个数
插入少查询多的话,最优参数其实是更接近 0.7 x / (x y) 的, 查询再多也不建议参数低于0.7
当然最优参数的话,有时间可以自己去测试调整
<2>其实update函数那里吧两个 if 直接写成 max / min 未必时间会差一点,可以自己尝试测试时间
学习资料在这里
对于k维kdtree,实际时间复杂度为o(n*n^(1-1/k))
build实现类似spaly,不断选一维从中间划分,可以循环选取维度,也可以rand % k
求最近点的query,重点在于其中的估价函数
求区间和的query,则和线段树类似
例题:
1) bzoj 2648求最近点
1 /*为了维持树的平衡,可以一开始把所有点都读进来build 2 然后打flag标记该点是否被激活*/ 3 #include3种写法可选
<1>插入的时候采用替罪羊树的方法来维护,子树太偏就直接重构
<2>一开始直接把所有点扔进kdtree,用flag来标记该点是否被激活
<3>直接插入不重构。事实上本题没有出极端数据导致树会特别偏,所以可过。这种写法需要稍稍注意常数
2) bzoj 4066 二维平面,单点修改,区间查询,强制在线
1 #include正直写法,子树size过大就要rebuild
判断rebuild的系数取的0.75,注意rebuild不是在回溯过程中当前子树不平衡就立刻重构
而是回溯过程中找到最靠近根的需要重构的子树根节点,然后对这棵树进行重构
详情看代码
不正直写法,新节点先存在数组里,查询时遍历该数组 查询kdtree
当数组size大于k时,把数组里的点放进kdtree并重构整颗kdtree
k取10000即可,可过
3) bzoj4154 二维平面,区间覆盖,单点查询
1 #include重点在于观察到修改的是一定距离以内的子节点
所以考虑转换到二维平面,一维为dfs序,一维为dep
通过子树区间 深度区间限制,即可得到修改的点集
二维平面上区间覆盖 单点查询,kdtree即可
4) bzoj3489 求区间内最大的只出现过一次的数
1 #include考虑a[i]上一次出现,和下一次出现位置分别pre[i], suc[i]
即变成pre[i], i, suc[i]在[0, l - 1], [l, r], [r 1, n 1]之间的数的最大值
3维kdtree即可,注意也要使用ans与区间最值的关系来优化
注意到3维有不需要的变量,不需要维护,40s时限跑了39s ...
简单总结:
1) 解决求最值的问题,kdtree其实就是优化暴力,所以一般都需要估价函数优化才可以
2) 非最值问题,其实感觉更像线段树,但由于奇妙的原因,所以一次更新覆盖到的区间可能达到o(sqrt(n))个
3) 由于本质是暴力,所以常数比较重要,inline,子树非空才更新,估价函数,无用信息不更新...各种优化,一般而言10w已经是极限
4) 板子题不说, kdtree的题目需要从题中找到k维平面,然后区间修改和区间查询就完事了
5) 非强制在线题目,并不优先考虑kdtree,可以考虑树套树,kdtree不强制在线可以考虑排序降维
其他例题
5) bzoj4520 给n个点,求第k远点对
solution:可以看到k比较小,所以所有点建好kdtree,对每个点查询一下,维护出一个大小为k的堆
把n个点的堆进行合并,和并出大小为2k的堆,即得到答案
6) hdu 5992
可以考虑裸的3维kdtree,能过
考虑到n比较大,不强制在线,可以按价格排序,带插入的kdtree,需要重构
这个题我打训练赛写后一种排序重构写法t到自闭
然后把那个重构的参数调整成了0.95就过了...
后来仔细考虑了一下,这个题的特点在于n很大,m不大
20w个插入,只有2w个查询
如果正常的把参数设为0.75的话,实际效率是接近o(n^(3/2))的
但参数设的比较偏的话,会减少重构次数,查询的复杂度会增加
但实际总复杂度更接近o(m*n(1/2)),所以就能a了
所以通过这个题,我们推荐重构参数设为 m/(n m) 0.5
转载于:https://www.cnblogs.com/ytytzzz/p/9625618.html
总结
- 上一篇:
- 下一篇: