珂朵莉树 范围查询 chtholly tree Old driver tree

因为我没看过这个动画,所以还是备注一下。珂朵莉是珂朵莉·诺塔·瑟尼欧里斯,轻小说《末日时在做什么?有没有空?可以来拯救吗?》及其衍生作品中登场的女主角,成体妖精兵之一。最强圣剑“瑟尼欧里斯”的适合者。原题是 Codeforce 上的一道区间有关的题目。Chtholly Tree 又名 Old driver Tree。

这个锯树结构其实并不是数据结构,只是一个 set 的用法二一。

首先是基本的思想,实现老司机树,只需要一个 `std::set` 以及熟悉 `std::set::upper_bound` 和一些其他特性。 但是需要注意的地方包括迭代器失效。

 老司机树的核心思想、范围查询的核心思想

对范围(区间)进行有序保存;过程中对区间进行分割,分割后顺序删除原来的零碎区间;最后合并一个大区间。

下面列出一些需要注意的 C++ 要点。第一个要点是 std::set 的性能问题。一般 std::set 可能会用作顺序容器使用(虽然顺序访问的复杂度很危险,但是对于查找具有 logn 时间非常好用,也就是,偶尔的内容修改)。 `std::set` 的参与排序是通过 `operator <` 就能提供支持了。

 C++ 中如果想修改 std::set 中的 元素而不用删除他重新插入(删除的 rebalance 费时间)要怎么做?

使用 mutable 属性,只要不是 set 中参与排序的数据即可。

然后是怎么进行区间的分裂。我们先复习各种二分查找先吧。

 二分查找的 lower_bound 和 upper_bound 中,如何查找以下情况:(1)第一个大于,(2)最后一个小于等于,(3)第一个等于。等等。。。

注意,ODT 树种用的是那种,要看具体情况具体分析。这里考虑如果最后一个小于等于。我们用 `upper_bound -1` 来实现这个需求。

对于需要合并一个 (l, r) 的区间,

  • 首先,得到 (l+k, ...) k大于等于0 的区间迭代器,和 (r+k, ...) k 大于等于0 的区间迭代器
  • 然后删除所有两个迭代器直接的节点,然后插入一个(l,r) 的节点。

然后补充一下对于最后一个小于的情况,如果不包含 r,如何进行分裂呢?

我们考虑普通的增量做法,目前已经有:

[1, 5] 和 [10, 16]

此时尝试分裂 6,upper_bound-1 得到的是 [1,5] 这个区间,此时分裂实际会删除 [1,5] 插入一个新的 [1, 6] 节点 和 [6, 5] 节点。

这是不可行的。因此我们只能用全量的做法!

如果存在 [1,5] 和 [10,16], 那么我们整体必须有(如果 boundary 是 20 的话):

([1, 5]: true), ([6, 9]: false), ([10, 16]: true), ([17, 20]: false)

这样的排列,从而保证查找 6 进行分裂的时候,会直接返回 6,因为 6 必定存在。如果 7 不存在,那么可能是这种情况:

([1, 7]: true), ([8, 9]: false), ([10, 16]: true), ([17, 20]: false)

即 6 必然被前一个包括了。

剩下一个查询没有说明,读写都可以 split 套模板,但读操作确实优化,比如例子,查找 【1,6】,只需读,6 性质和 【5,7】同样的,不用分裂:

([1, 2]: true), ([3, 4]: false), ([5, 7]: true), ([8, 20]: false)

总结修改访问的套路模板:

void performance(int l, int r) {

  auto itr = split(r + 1), itl = split(l);

  for (; itl != itr; ++itl) {

    // Perform Operations here

  }

}

注:珂朵莉树在进行求取区间左右端点操作时,必须先 split 右端点,再 split 左端点。若先 split 左端点,返回的迭代器可能在 split 右端点的时候失效,可能会导致 RE。

 set 的迭代器失效问题

实际 set 的迭代器不会失效,除非你删除了他。

总结一下 Chtholly Tree 的思路:

  • 所有的区间无论 true false 还是什么性质,都要整体存在,因此一开始是整个 domain 都是相同的性质。
  • 每次对区间的修改,都会造成分裂。
  • 区间操作先 split 右边再 split 左边。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值