log 理论

为何你的代码总是常数大?快来学习 \(\log\) 理论,看看究竟是哪里无形之中产生了大常数。

帮你理解各种 \(O(\log)\) 常数。

以下算法大体按照常数从小到大排序。

小:跑不满 \(\log\)

中:大概会跑到 \(\log\)

大:会多于 \(\log\)

巨:约等于 \(\log^2\)

数据结构

\(\log\)

  1. 树剖/dsu on tree
  2. 树状数组
  3. 李超线段树

\(\log\)

  1. 普通 zkw 线段树

\(\log\)

  1. 普通线段树

超级巨 \(\log\)

  1. 主席树/其他动态开点线段树
  2. map/set
  3. 普通平衡树(FHQ)

注:以上线段树均指 pushuppushdown 都很简短的线段树。

附:线段树各种操作的常数

单点操作/查询:中 \(\log\)

区间操作/查询:大 \(\log\)

全局二分:大 \(\log\)

区间二分:巨 \(\log\)(born_to_sun 版本)

线段树合并:巨 \(\log\)

可持久化线段树合并:超级巨 \(\log\)

更精细的常数,参见 数据结构的精细复杂度

算法

\(\log\)

  1. 启发式分裂
  2. 调和级数
  3. 启发式合并
  4. Boruvka

\(\log\)

  1. \(\operatorname{sort}\)
  2. \(^{[1]}\)
  3. lower_bound
  4. 手写二分
  5. 点分治/点分树
  6. cdq 分治
  7. st 表预处理

\(\log\)

  1. 边分治
  2. SA

注:\([1]\),如果堆一直 push,很快,但是 pop 就很慢,原因不详。堆的空间常数可以粗略认为是 vector,因为堆的底层时限是 vector。

STL

  1. vector

    一个或很少个数的 vector.push_back,很快。

    多个 vector.push_back,访问较为连续,一般。

    多个 vector.push_back,随机访问,很慢。

  2. set

    和 vector 类似,但是因为复杂度带 \(\log\),当插入元素个数一定时,常数关于容器个数呈单峰函数。

后记

部分算法因为核心思想不同,无法相互取代,常数大小很难比较。比如让我比较边分治和 SA 的常数就很困难,但 sort,堆,lower_bound 在某些条件下可以相互取代,因此常数能够比较。

posted @ 2025-04-15 19:14  born_to_sun  阅读(54)  评论(0)    收藏  举报