压位trie
一种亚 log 数据结构。
在 01trie 二叉树的基础上,变成 \(w\) 叉树,一般取 \(w=64\) 方便用 ull 存储子树是否存在的情况。
支持查询前驱后继最小最大值和插入删除,复杂度都是 \(O(log_{w}V)\) 的,一般的 \(10^9\) 值域也就是 \(5\) 左右的常数。和堆相比功能强大,复杂度也小很多。常用于奇怪的不正确复杂度算法的卡常(如带 \(\log\) 莫队加上这个可以看成大常数的 \(n\sqrt{n}\))。
空间上,因为最底下一层儿子不需要存子树是否存在,所以这部分只需要动态开 \(cnt\) 记录出现次数即可。空间复杂度 \(O(n+\frac{V}{w})\)。值域 \(10^9\) 的话就是 \(125MB\) 多一些这个样子。
实现上类似于 01trie,插入删除是简单的,查前驱后继可以从下往上跳,看左/右边是否存在子树,存在就跳进去找最小/大值即可。最小最大值也是简单的。
插入的时候如果不需要存出现次数的计数器,那么可以判断此数是否已经插入。
删除的时候可以看当前的节点是否还有别的子树,有的话就直接结束过程不用向上跳了。
总之是个不难写的东西,因为常用于卡常,写的时候注意多做位运算代替普通运算和剪枝来减小常数。
例:P8078 WC题,\(O(n\sqrt{n} \log n)\) 的暴力莫队是 naive 的,可以用 set 维护。
发现需要支持查前驱后继和对应值,那么实现一个压位 trie 就轻松做到 \(O(n\sqrt{n} \log_{64}n)\) 了。空间也是 \(O(n)\) 的。
因为大多数人都拿的是回滚莫队做的题,而回滚莫队的常数懂的都懂,所以只要压位 trie 实现的常数好就基本能过,至少也能只 T 一两个点。