这是我个人的算法竞赛模板库。
大多数代码可以在 (gcc, C++11) , (clang, C++11) 和 (msvc, C++14) 环境下编译运行。如有特殊情况会注明。
由于 C++17 的 std::gcd/std::lcm 以及 C++20 的位运算使用较多,所以本模板库内置了 std::bit.h 和 std_gcd_lcm.h 两个头文件。语言版本较低的使用者在导入这两个头文件之后,就可以使用 std::gcd/std::lcm/std::popcount 等。
-
速度极快
RMQ问题是算法竞赛中常见的问题。如果带修,那么需要使用线段树来维护,以$O(\log n)$ 的时间复杂度进行修改和查询。在
atcoder运行benchmark显示,本模板库的OY::MonoMaxZkw<uint32_t>在n = 1e6的情况下,一秒钟可以做3.3e7次区间最值查询。惊人的速度。 -
使用方便
FOR宏定义?i64缩写?编程老手都认识?No!本模板库中,不会使用这些花里胡哨的缩写,也不会假定使用者是老手。本模板,让任何码风的人都不会感到不适应。模板高度封装,使用的时候可以当成黑盒,而不需要对模板内部做手脚。每份代码都有对应的文档,提供使用范例;
TEST文件夹里,提供了本地运行代码,以及在若干oj题目上提交时的代码。
-
大值域的线性空间线段树(
CompressedTree维护0~1e18范围的权值线段树,空间复杂度正比于操作次数) -
动态大小的
bitset(DynamicBitset效率与静态大小的bitset持平) -
可以动态维护全局半群信息的双向队列(
Deque完爆std::deque) -
可以维护区间加定值、区间加一次函数、区间加
k次函数的树状数组 (KBIT) -
当只有邻项合并操作时,严格线性时间复杂度的并查集(
LinearDSU) -
线性时间初始化,
$O(1)$ 查询区间最值的状压RMQ(MaskRMQ) -
支持区间翻转、区间剪切合并,同时维护区间半群信息的平衡树(
MonoAVL,MonoSplay) -
查询速度极快的单点修改、区间查询线段树(
MonoZkw) -
二维、三维以及更高维度上,维护半群信息(可以带修)的多维表/树(
MultiDimSegTree) -
通过编写
node以实现自定义操作的势能线段树(SegmentBeat) -
支持区间排序,并维护区间半群信息的线段树/平衡树 (
SortFHQ,SortSeg) -
线性时间初始化, 平均
$O(1)$ 查询区间半群信息的根树(SqrtTree) -
单点加修改、区间和查询速度极快,碾压树状数组的数据结构 (
WTree) -
各种存储结构和算法执行器独立分开的图模板;
-
动态/静态模数的、32位/64位模数的、使用/不使用蒙哥马利模乘的
modint; -
运用了蒙哥马利模乘的运行极快的质数判定(
PrimeCheck) -
运用了蒙哥马利模乘的运行极快的因数分解(
Pollard Rho) -
支持基于范围遍历的数论分块 (
SqrtDecomposition) -
支持自定义个数、种类的模数的序列哈希(
SequenceHash,SequenceMultiHash) -
支持回滚操作的
KMP,回文自动机; -
线性时间复杂度的后缀数组;
-
通过编写
node以实现自定义操作的动态树(GBT,LCT) -
结合其他模板,快速制造出各种树套树数据结构的控制器(
LeveledBITManipulator,LeveledZkwManipulator,LeveledSegManipulator) -
高度抽象的数位
dp模板; -
线性空间同时维护最大值和最小值的堆(
MinMaxHeap); -
支持对交换幺半群进行子矩形修改、子矩形查询的二维线段树;
-
实用的
leetcode输入输出工具,支持网页端的同样格式的输入输出数据。
-
我的编程环境非常老旧,看到你的模板库代码花里胡哨的,能运行起来吗?
本模板库现已放宽对语言环境的要求,绝大多数模板,
gcc和clang在C++11之后均可使用;MSVC在C++14之后均可使用(暂无C++11测试环境)。只要你的C++语言标准在C++11之后,均可以使用我的模板库。 -
在
LCT和GBT等一些模板里,看到MAX_NODE参数,这是什么意思?在这些模板里,通过
MAX_NODE控制全局内存池的大小,,从这个全局内存池向不同的树对象分配结点。 -
在模板里,填写的
MAX_NODE是否越大越好?如果是多组测试,是否每组测试重新构造一个数据结构对象就会触发$O(MAXNODE)$ 的初始化导致超时?以下回答针对你的结点类型为平凡类型的情况(无构造函数,无初始值)。
MAX_NODE相关联的是结点内存池的大小,所以并不会出现每次构造一个数据结构对象,就导致内存池初始化的情况。MAX_NODE并非越大越好,当MAX_NODE过大时,编译可能会失败。只要编译能通过,那么在此范围内MAX_NODE多大都没关系,也不会有任何的时间开销。 -
线段树只能有求和的功能吗?
本模板库非常重视模板的泛化程度。
一般来说,包括线段树在内的维护半群的数据结构,天然带有
Min,Max,Gcd,Lcm,BitAnd,BitOr,BitXor以及Sum这八种实例类型。如果有额外的需求,可以通过make_系列来定义新的类型;或者传递自定义的Monoid半群类型。其他的容器也往往如此。 -
线段树模板参数一大堆,填写起来老是报错?连类型名字都不能完整写出来该怎么办?
为了防止定义各种千奇百怪运算符的使用者在使用模板时,因为无法描述出模板的完整类型名称而困扰,所以特意编写了
make_系列函数。如同std::make_pair以及std::make_tuple一般,只需要填写少量参数即可创建出复杂类型的模板。例如,make_SegTree可以用来创建线段树;只要打出make_SegTree之后跟随IDE的智能提示进行相应的填写即可。 -
用
make_SegTree可以创建一颗线段树;但是如果我要在std::vector里存放十颗线段树,我还是得把类型全称写出来,可是我写不出来,怎么办?既然用
make_SegTree可以创建出一颗线段树,那么可以用using NickName = decltype(make_SegTree<...>(...));来捕获这棵树的类型,并给它起个别名。接下来即可用std::vector<NickName>的方式存储十颗线段树。 -
为什么使用
StaticModInt64,DynamicModInt64取模结果出错?本模板库要求
gcc或者clang编译器的long double具有80个bit的size;可以通过std::numeric_limits<long double>::max()检查输出是否为pow(10, 4932)以上。如果不够,那么基于long double进行的计算就可能因精度不足而出错。一般而言,
MSVC编译器的long double只有64个bit的size,所以在MSVC环境里往往不使用long double进行计算,而是通过其他算法进行计算。所以,MSVC编译器的long double位数不足不会导致计算结果错误。
