From 2c053393a508ea9539bea14dcd754761c0851acd Mon Sep 17 00:00:00 2001 From: Tifa <62847935+Tiphereth-A@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:23:00 +0800 Subject: [PATCH] feat: upload --- source/_posts/about.md | 5 + source/_posts/academic-waste.md | 3 +- source/_posts/draft-020.md | 89 ++++ source/_posts/drafts.md | 1 + .../{statements-cn.pdf => statements-zh.pdf} | Bin source/_posts/rbtree.md | 68 +++ ...tree-test2.md => zkw-segment-tree-test.md} | 8 +- source/_posts/zkw-segment-tree-test1.md | 191 --------- source/_posts/zkw-segment-tree.md | 36 +- source/code/draft-020/pfactors.cpp | 32 ++ source/code/rbtree/rbtree.hpp | 399 ++++++++++++++++++ .../bit.cpp | 0 .../data_gen.cpp | 0 .../seg-r.cpp | 0 .../seg-zkw.cpp | 3 +- source/code/zkw-segment-tree-test1/bit.cpp | 102 ----- source/code/zkw-segment-tree-test1/seg-r.cpp | 130 ------ .../code/zkw-segment-tree-test1/seg-zkw.cpp | 132 ------ 18 files changed, 601 insertions(+), 598 deletions(-) create mode 100644 source/_posts/draft-020.md rename source/_posts/icpc-anjr2023/{statements-cn.pdf => statements-zh.pdf} (100%) create mode 100644 source/_posts/rbtree.md rename source/_posts/{zkw-segment-tree-test2.md => zkw-segment-tree-test.md} (86%) delete mode 100644 source/_posts/zkw-segment-tree-test1.md create mode 100644 source/code/draft-020/pfactors.cpp create mode 100644 source/code/rbtree/rbtree.hpp rename source/code/{zkw-segment-tree-test2 => zkw-segment-tree-test}/bit.cpp (100%) rename source/code/{zkw-segment-tree-test2 => zkw-segment-tree-test}/data_gen.cpp (100%) rename source/code/{zkw-segment-tree-test2 => zkw-segment-tree-test}/seg-r.cpp (100%) rename source/code/{zkw-segment-tree-test2 => zkw-segment-tree-test}/seg-zkw.cpp (99%) delete mode 100644 source/code/zkw-segment-tree-test1/bit.cpp delete mode 100644 source/code/zkw-segment-tree-test1/seg-r.cpp delete mode 100644 source/code/zkw-segment-tree-test1/seg-zkw.cpp diff --git a/source/_posts/about.md b/source/_posts/about.md index 3a99d77e77..e2b0b1f83b 100644 --- a/source/_posts/about.md +++ b/source/_posts/about.md @@ -13,6 +13,11 @@ date: 2020-05-04 19:07:23 +## 目录汇总 + +- {% post_link drafts 随笔 %} +- {% post_link academic-waste 学术垃圾 %} + ## 本博客采用的部分非通用记号/名称等 | 记号 | 含义 | 例子 | diff --git a/source/_posts/academic-waste.md b/source/_posts/academic-waste.md index 286c6bf2ad..40f08b49fb 100644 --- a/source/_posts/academic-waste.md +++ b/source/_posts/academic-waste.md @@ -29,6 +29,5 @@ date: 2023-04-01 09:06:31 - {% post_link setth-04 %} - {% post_link symmetric-bilinear-metric-space-and-system-of-linear-equations %} - {% post_link thpoker %} -- {% post_link zkw-segment-tree-test1 %} -- {% post_link zkw-segment-tree-test2 %} +- {% post_link zkw-segment-tree-test %} - {% post_link zkw-segment-tree %} diff --git a/source/_posts/draft-020.md b/source/_posts/draft-020.md new file mode 100644 index 0000000000..352f7f422f --- /dev/null +++ b/source/_posts/draft-020.md @@ -0,0 +1,89 @@ +--- +title: "随笔 - Miller-Rabin + Pollard-Rho 分解质因子的时间复杂度分析" +date: 2024-11-29 20:43:13 +categories: + - 随笔 + - 算法竞赛 +tags: + - 随笔 + - 算法竞赛 + - 数学 + - 数论 + - Miller-Rabin算法 + - Pollard-Rho算法 + - 素数/质数 + - 素性检验 + - Ramanujan和 + - 凹函数 + - Jensen不等式 +--- + +省流版: $O\left(n^{1/4}\right)$ + + + +我们考虑这样的代码 + +{% icodeweb blog lang:python draft-020/pfactors.cpp %} + +其中 + +- `is_prime_miller_rabin` 为基于 Miller-Rabin 算法的质数判断, 时间复杂度为 $O\left(n^{1/4}\right)$ +- `pollard_rho` 为 Pollard-Rho 算法, 返回入参的一个非平凡因子, 期望时间复杂度为 $O\left(p^{1/2}\right)$, 其中 $p$ 为 $n$ 的最小质因子, 不难发现 Pollard-Rho 算法的期望时间复杂度为 $O\left(n^{1/4}\right)$ +- 回调函数 `callback` 的时间复杂度为 $O(1)$ + +我们尝试计算 `pfactors` 的时间复杂度, 设其为 $O(T(n))$, 则有 + +$$ +O(T(n)) = \begin{cases} + O\left(n^{1/4}\right),&n<2\lor n\in\mathbb{P},\\ + O\left(n^{1/4}+T(d)+T(n/d)\right),&\text{otherwise}, +\end{cases} +$$ + +其中 $d$ 为 $n$ 的某个非平凡因子, $T(d)$ 没法直接处理, 我们用均值代替: + +$$ +\begin{aligned} + T(d)+T(n/d)&\xlongequal{\exists C>0}C\dfrac{\sum_{10$ 时 + +$$ +\sigma_k(n)=\zeta(k+1)n^k\sum_{m=1}^{\infty}\frac{c_m(n)}{m^{k+1}}\tag{2} +$$ + +其中 $c_q(n)=\displaystyle\sum_{1\leq a\leq q;(a,q)=1}\mathrm{e}^{(2\pi\mathrm{i}an)/q}$ 为 [Ramanujan 和](https://en.wikipedia.org/wiki/Ramanujan_sum), 所以这个看起来是有搞头的, 不过 $(2)$ 式涉及到级数, 看起来就不好用, 所以我们不会用 $(2)$ 式去证 $(1)$ 式, 而是一个更简单的做法 + +注意到 $f(x)=x^{1/4}$ 是凹函数, 所以我们考虑 Jensen 不等式 + +$$ +\begin{aligned} + \dfrac{\sum_{d\mid n}d^{1/4}}{\sum_{d\mid n}1}&\leq\left(\dfrac{\sum_{d\mid n}d}{\sum_{d\mid n}1}\right)^{1/4}\\ + &=\left(\dfrac{\sigma_1(n)}{\sigma_0(n)}\right)^{1/4}\\ + &=O\left(\left(\dfrac{n\log\log n}{\log n}\right)^{1/4}\right)\\ + &\xlongequal{\exists\epsilon>0} O\left(n^{1/4-\epsilon}\right) +\end{aligned} +$$ + +综上所述, `pfactors` 的时间复杂度为 $O\left(n^{1/4}\right)$ diff --git a/source/_posts/drafts.md b/source/_posts/drafts.md index b531a181bf..d89154560d 100644 --- a/source/_posts/drafts.md +++ b/source/_posts/drafts.md @@ -34,3 +34,4 @@ date: 2020-05-20 11:06:32 - {% post_link draft-017 %} - {% post_link draft-018 %} - {% post_link draft-019 %} +- {% post_link draft-020 %} diff --git a/source/_posts/icpc-anjr2023/statements-cn.pdf b/source/_posts/icpc-anjr2023/statements-zh.pdf similarity index 100% rename from source/_posts/icpc-anjr2023/statements-cn.pdf rename to source/_posts/icpc-anjr2023/statements-zh.pdf diff --git a/source/_posts/rbtree.md b/source/_posts/rbtree.md new file mode 100644 index 0000000000..0a2bc4edda --- /dev/null +++ b/source/_posts/rbtree.md @@ -0,0 +1,68 @@ +--- +title: 模板 - 红黑树 +categories: + - 笔记 +tags: + - 数学 + - 模板 + - 数据结构 + - 平衡树 + - 顺序统计树 + - 二叉搜索树 + - 红黑树 +date: 2024-11-27 09:23:57 +--- + +红黑树是一种平衡树, 是 C++ `std::(multi)?(set|map)`, Java `Tree(Set|Map)` 的底层实现 + +代码参考了 `pb_ds` 的设计方式, 时空均略优于 `pb_ds` + +{% note info %} +这里的代码实际上是 order-statistic tree, 即每个结点都记录了对应子树的大小, 因此支持查找排名以及根据排名反查数据 +{% endnote %} + +{% note warning %} +仅在 GCC 下测试过 +{% endnote %} + +{% note warning %} + 存放了笔者对该算法/数据结构的最新实现, 建议前往此处查看相关代码 +{% endnote %} + + + +## 设计与使用 + +参考了 `pb_ds` 的设计方式, 使用了 Mixin Classes, `balance_tree` 即为二叉搜索树, `balance_tree` 即为红黑树 + +具体来说, 代码中把一般的平衡树/顺序统计树操作 (遍历, `(lower|upper)_bound`, `order_of_key`, `find_by_order` 等) 和红黑树的操作 (旋转, 插入和删除的性质维护等) 解耦. 又由于红黑树的插入/删除可以视为先按二叉搜索树的方式插入/删除, 再进行平衡维护操作, 故代码中也将这两部分解耦, 并让 `rbt_tag` 继承 `bst_tag` 来提高代码复用率 + +使用方式类似 `pb_ds` 的 `__gnu_pbds::tree`, 只是没有将维护子树大小的部分解耦出来 + +## 代码 + +
+Show code + +{% icodeweb blog lang:cpp rbtree/rbtree.hpp %} + +
+ +## 示例 + +洛谷 P6136 [【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) + +
+Show code + +{% icodeweb cpa_cpp title:Luogu_P6136 Luogu/P6136/3.cpp %} + +
+ +--- + +## 参考 + +- +- Introduction to Algorithms, Fourth Edition +- diff --git a/source/_posts/zkw-segment-tree-test2.md b/source/_posts/zkw-segment-tree-test.md similarity index 86% rename from source/_posts/zkw-segment-tree-test2.md rename to source/_posts/zkw-segment-tree-test.md index 22c9edf350..b582526df0 100644 --- a/source/_posts/zkw-segment-tree-test2.md +++ b/source/_posts/zkw-segment-tree-test.md @@ -40,7 +40,7 @@ comments: false
Show code - {% icodeweb blog lang:cpp zkw-segment-tree-test2/seg-r.cpp %} + {% icodeweb blog lang:cpp zkw-segment-tree-test/seg-r.cpp %}
@@ -49,7 +49,7 @@ comments: false
Show code - {% icodeweb blog lang:cpp zkw-segment-tree-test2/seg-zkw.cpp %} + {% icodeweb blog lang:cpp zkw-segment-tree-test/seg-zkw.cpp %}
@@ -58,7 +58,7 @@ comments: false
Show code - {% icodeweb blog lang:cpp zkw-segment-tree-test2/bit.cpp %} + {% icodeweb blog lang:cpp zkw-segment-tree-test/bit.cpp %}
@@ -67,7 +67,7 @@ comments: false
Show code - {% icodeweb blog lang:cpp zkw-segment-tree-test2/data_gen.cpp %} + {% icodeweb blog lang:cpp zkw-segment-tree-test/data_gen.cpp %}
diff --git a/source/_posts/zkw-segment-tree-test1.md b/source/_posts/zkw-segment-tree-test1.md deleted file mode 100644 index 596102b13c..0000000000 --- a/source/_posts/zkw-segment-tree-test1.md +++ /dev/null @@ -1,191 +0,0 @@ ---- -title: 「线段树的扩展之浅谈zkw线段树」一文的测试代码、数据、详细测试结果与生成器 -tags: - - 算法竞赛 - - 洛谷日报 - - 测试 -date: 2018-08-08 13:05:56 -comments: false ---- - -## 测试代码 - - - -> 注意: 因为笔者脑抽并没有调用 fread - -### 递归线段树 - -
-Show code - -{% icodeweb blog lang:cpp zkw-segment-tree-test1/seg-r.cpp %} - -
- -### zkw 线段树 - -
-Show code - -{% icodeweb blog lang:cpp zkw-segment-tree-test1/seg-zkw.cpp %} - -
- -### 树状数组 - -
-Show code - -{% icodeweb blog lang:cpp zkw-segment-tree-test1/bit.cpp %} - -
- -## 详细评测结果#1(以秒为单位) - -```text -test#1: -time#1 = 3.447280 -time#2 = 3.696011 -time#3 = 3.683904 -time#4 = 3.648302 -time#5 = 3.296298 -time#6 = 7.708645 -time#7 = 7.082259 -time#8 = 7.126166 -time#9 = 6.963065 -time#10 = 7.756587 -time#11 = 51.682691 -time#12 = 48.633226 -time#13 = 47.932671 -time#14 = 105.835116 -time#15 = 113.755264 -time#16 = 158.988079 -Ave: -3.554359 -7.327344 -49.416196 -126.192820 - -test#2: -time#1 = 2.511897 -time#2 = 1.695306 -time#3 = 2.118245 -time#4 = 2.079538 -time#5 = 1.934902 -time#6 = 4.719447 -time#7 = 4.817881 -time#8 = 4.623501 -time#9 = 5.097146 -time#10 = 5.355650 -time#11 = 34.865590 -time#12 = 34.907769 -time#13 = 32.463151 -time#14 = 74.738552 -time#15 = 73.855234 -time#16 = 74.000259 -Ave: -2.067978 -4.922725 -34.078837 -74.198015 - -test#3: -time#1 = 2.060278 -time#2 = 1.992258 -time#3 = 2.002796 -time#4 = 1.820297 -time#5 = 1.964742 -time#6 = 4.490217 -time#7 = 4.383801 -time#8 = 4.159582 -time#9 = 4.358566 -time#10 = 4.404194 -time#11 = 26.816804 -time#12 = 26.666548 -time#13 = 26.862968 -time#14 = 57.438784 -time#15 = 57.584326 -time#16 = 57.433181 -Ave: -1.968074 -4.359272 -26.782107 -57.485430 - -``` - -## 详细评测结果#2(以秒为单位) - -```text -test#1: -time#1 = 3.723513 -time#2 = 4.895209 -time#3 = 4.096053 -time#4 = 3.921210 -time#5 = 3.291190 -time#6 = 6.779961 -time#7 = 7.278650 -time#8 = 7.048792 -time#9 = 6.539919 -time#10 = 7.330734 -time#11 = 45.730535 -time#12 = 45.291213 -time#13 = 45.184196 -time#14 = 99.981685 -time#15 = 99.947381 -time#16 = 99.487397 -Ave: -3.985435 -6.995611 -45.401981 -99.805488 - -test#2: -time#1 = 1.830346 -time#2 = 2.099576 -time#3 = 2.085104 -time#4 = 2.171504 -time#5 = 2.239576 -time#6 = 4.618806 -time#7 = 3.836848 -time#8 = 4.119962 -time#9 = 4.149363 -time#10 = 4.619962 -time#11 = 29.606656 -time#12 = 29.431768 -time#13 = 29.710447 -time#14 = 67.893993 -time#15 = 66.520776 -time#16 = 68.217186 -Ave: -2.085221 -4.268988 -29.582957 -67.543985 - -test#3: -time#1 = 1.943302 -time#2 = 2.128660 -time#3 = 2.122796 -time#4 = 1.828443 -time#5 = 1.882570 -time#6 = 3.893438 -time#7 = 4.130842 -time#8 = 3.821628 -time#9 = 3.872965 -time#10 = 4.239745 -time#11 = 25.007103 -time#12 = 25.088401 -time#13 = 25.442503 -time#14 = 54.489850 -time#15 = 54.187768 -time#16 = 54.235232 -Ave: -1.981154 -3.991724 -25.179336 -54.304283 -``` - -{% post_link zkw-segment-tree 回到原文 %} diff --git a/source/_posts/zkw-segment-tree.md b/source/_posts/zkw-segment-tree.md index b92743e276..e0fa31334f 100644 --- a/source/_posts/zkw-segment-tree.md +++ b/source/_posts/zkw-segment-tree.md @@ -289,7 +289,7 @@ _~~zkw:狗拿耗子, 猫下岗了~~_ ## 大数据测试 -{% post_link zkw-segment-tree-test2 测试已更新 %} +另请参阅: {% post_link zkw-segment-tree-test %} 先来看一看参赛选手: @@ -323,40 +323,6 @@ _~~zkw:狗拿耗子, 猫下岗了~~_ 可以看到, 没有 O2 时 2 号和 3 号相差无几, 有了 O2 之后 3 号吊打全场~~可能是笔者写的 zkw 线段树常数太大 QwQ~~ -为了~~防止 zkw 线段树被吊打得太惨~~反应算法真实水平以及模拟 NOIp 竞赛环境, 下面就不开 O2 了 - -在这里先放一下结果, 测试代码和大数据放在 {% post_link zkw-segment-tree-test1 另一篇文章 %} 里 - -保证所有输入数据在 unsigned int64_t 范围内, 结果对 $2^{64}$ 取模, 表格中的时间为平均值 - -测试环境: - -> 系统:noilinux-1.4.1 -> -> 内存:2GB -> -> CPU:AMD Athlon(tm) II X4 631 Quad-Core Processor 2600 MHz - -- 测试#1: - -| 数据规模 | 递归线段树(ms) | zkw 线段树(ms) | 树状数组(ms) | -| :-------: | :------------: | :------------: | :----------: | -| 5e5(5 组) | 3554.359 | 2067.978 | 1968.074 | -| 1e6(5 组) | 7327.344 | 4922.725 | 4359.272 | -| 5e6(3 组) | 49416.196 | 34078.837 | 26782.107 | -| 1e7(3 组) | 126192.820 | 74198.015 | 57485.430 | - -- 测试#2: - -| 数据规模 | 递归线段树(ms) | zkw 线段树(ms) | 树状数组(ms) | -| :-------: | :------------: | :------------: | :----------: | -| 5e5(5 组) | 3985.435 | 2085.221 | 1981.154 | -| 1e6(5 组) | 6995.611 | 4268.988 | 3991.724 | -| 5e6(3 组) | 45401.981 | 29582.957 | 25179.336 | -| 1e7(3 组) | 99805.488 | 67543.985 | 54304.283 | - -结论:**不考虑有运算优先级的情况**下, 树状数组吊打全场(zkw 线段树哭晕在厕所 - ## 后记 这篇文章笔者写了~~将近一天~~整整三天 diff --git a/source/code/draft-020/pfactors.cpp b/source/code/draft-020/pfactors.cpp new file mode 100644 index 0000000000..a085778772 --- /dev/null +++ b/source/code/draft-020/pfactors.cpp @@ -0,0 +1,32 @@ +#include +using u64 = std::uint64_t; + +extern bool is_prime_miller_rabin(u64 n); +extern u64 pollard_rho(u64 n); + +template +void pfactors(u64 n, F callback) { + if (n < 2) return; + if (is_prime_miller_rabin(n)) { + callback(n); + return; + } + const u64 g = pollard_rho(n); + pfactors(n / g, callback), pfactors(g, callback); +} + +// examples: + +#include + +u64 get_max_prime_factor(u64 n) { + u64 max_pf = 0; + pfactors(n, [&](u64 p) { max_pf = std::max(max_pf, p); }); + return max_pf; +} + +std::vector get_all_prime_factors(u64 n) { + std::vector prime_factors; + pfactors(n, [&](u64 p) { prime_factors.push_back(p); }); + return prime_factors; +} diff --git a/source/code/rbtree/rbtree.hpp b/source/code/rbtree/rbtree.hpp new file mode 100644 index 0000000000..75b21a3fdf --- /dev/null +++ b/source/code/rbtree/rbtree.hpp @@ -0,0 +1,399 @@ +#include +using tree_size_t = uint32_t; + +class bst_tag { +protected: + template + auto size(pointer p) const -> tree_size_t { + return p ? p->sz : 0; + } + + /** + * Insert leaf node @param n to @param p + * + * @param root root of tree + * @param p parent of node which will be inserted + * @param n node which will be inserted + * @param dir direction of n, 0: left; 1: right + */ + template + void insert_leaf(pointer &root, pointer p, pointer n, bool dir) { + if (!p) { + root = n; + return; + } + p->ch[dir] = n, n->fa = p; + auto now = p; + while (now) now->sz++, now = now->fa; + } + /** + * Erase node @param n + * + * @param root root of tree + * @param n node which will be deleted, must have no more than 2 child + */ + template + void erase_branch_leaf(pointer &root, pointer n) { + auto p = n->fa, s = n->ch[0] ? n->ch[0] : n->ch[1]; + if (s) s->fa = p; + if (!p) { + root = s; + return; + } + p->ch[n->child_dir()] = s; + auto now = p; + while (now) now->sz--, now = now->fa; + } + + /** + * @param root root of tree + * @param p root of subtree (may be same as @param root) + * @param dir direction. 0: left rotate; 1: right rotate + */ + template + auto rotate(pointer &root, pointer p, bool dir) -> pointer { + auto g = p->fa; + auto s = p->ch[!dir]; // new root of subtree + assert(s); // pointer to true node required + s->sz = p->sz, p->sz = size(p->ch[dir]) + size(s->ch[dir]) + 1; + auto c = s->ch[dir]; + if (c) c->fa = p; + p->ch[!dir] = c, s->ch[dir] = p; + p->fa = s, s->fa = g; + if (g) g->ch[p == g->ch[1]] = s; + else root = s; + return s; + } +}; +class rbt_tag: protected bst_tag { +protected: + template + auto is_red(pointer p) const -> bool { + return p ? p->red : false; + } + + /** + * @param root root of tree + * @param p parent of node which will be inserted + * @param n node which will be inserted + * @param dir direction of n, 0: left; 1: right + */ + template + void insert_leaf(pointer &root, pointer p, pointer n, bool dir) { + n->red = p; + bst_tag::insert_leaf(root, p, n, dir); + // fix double red + while (is_red(p = n->fa)) { + bool p_dir = p->child_dir(); + auto g = p->fa, u = g->ch[!p_dir]; + // Case 1: both p and u are red + // clang-format off + // g [g] + // / \ / \ + // [p] [u] ==> p u + // / / + // [n] [n] + // clang-format on + if (is_red(u)) { + p->red = u->red = false; + g->red = true; + n = g; + continue; + } + // p is red and u is black + // Case 2: dir of n is different with dir of p + // clang-format off + // g g + // / \ / \ + // [p] u ==> [n] u + // \ / + // [n] [p] + // clang-format on + if (n->child_dir() != p_dir) rotate(root, p, p_dir), swap(n, p); + // Case 3: p is red, u is black and dir of n is same as dir of p + // clang-format off + // g p + // / \ / \ + // [p] u ==> [n] [g] + // / \ + // [n] u + // clang-format on + p->red = false, g->red = true; + rotate(root, g, !p_dir); + } + root->red = false; + } + /** + * @param root root of tree + * @param n node which will be deleted, must have no more than 2 child + */ + template + void erase_branch_leaf(pointer &root, pointer n) { + bool n_dir = n == root ? false : n->child_dir(); + bst_tag::erase_branch_leaf(root, n); + auto p = n->fa; + if (!p) { // n is root + if (root) root->red = false; + return; + } else { + auto s = p->ch[n_dir]; + if (s) { // n has 1 child + // n must be black and s must be red, so we need to color s black + s->red = false; + return; + } + } + // n is not root but leaf with black color, need to be fixup + while (p && !n->red) { + auto s = p->ch[!n_dir]; + // Case 1: s is red + // clang-format off + // p s + // / \ / \ + // |n| [s] ==> [p] d + // / \ / \ + // c d |n| c + // clang-format on + if (is_red(s)) { + s->red = false, p->red = true; + rotate(root, p, n_dir); + s = p->ch[!n_dir]; + } + // s must be black + auto c = s->ch[n_dir], d = s->ch[!n_dir]; + // Case 2: both c and d are black + // clang-format off + // {p} p + // / \ / \ + // |n| s ==> |n| [s] + // / \ / \ + // c d c d + // clang-format on + if (!is_red(c) && !is_red(d)) { + s->red = true; + n = p; + goto end_erase_fixup; + } + // Case 3: c is red and d is black + // clang-format off + // {p} {p} + // / \ / \ + // |n| s ==> |n| c + // / \ \ + // [c] d [s] + // \ + // d + // clang-format on + if (!is_red(d)) { + c->red = false, s->red = true; + rotate(root, s, !n_dir); + s = p->ch[!n_dir], c = s->ch[n_dir], d = s->ch[!n_dir]; + } + // Case 4: d is red + // clang-format off + // {p} {s} + // / \ / \ + // |n| s ==> p d + // / \ / \ + // {c} [d] |n| {c} + // clang-format on + s->red = p->red, p->red = d->red = false; + rotate(root, p, n_dir), n = root; + end_erase_fixup: + p = n->fa; + if (!p) break; + n_dir = n->child_dir(); + } + n->red = false; + } +}; + +template +struct balance_tree_node_t {}; +template +struct balance_tree_node_t { + balance_tree_node_t *fa; // == nullptr if root of the tree + balance_tree_node_t *ch[2]; // == nullptr if child is empty + // The index is: + // LEFT := 0, if (key < fa->key) + // RIGHT := 1, if (key > fa->key) + data_t data; + tree_size_t sz; // size of subtree + + /** + * @return child direction of this non-root point + */ + auto child_dir() const -> bool { return this == fa->ch[1]; } +}; +template +struct balance_tree_node_t { + balance_tree_node_t *fa; // == nullptr if root of the tree + balance_tree_node_t *ch[2]; // == nullptr if child is empty + // The index is: + // LEFT := 0, if (key < fa->key) + // RIGHT := 1, if (key > fa->key) + data_t data; + tree_size_t sz; // size of subtree + bool red; // true if node is red, otherwise black + + /** + * @return child direction of this non-root point + */ + auto child_dir() const -> bool { return this == fa->ch[1]; } +}; + +template , + template typename allocator_t = std::allocator> +struct balance_tree: public tree_tag_t { + using node_t = balance_tree_node_t; + using pointer = node_t *; + using const_pointer = const node_t *; + using pointer_const = node_t * const; + + static constexpr compare_t compare{}; + allocator_t alloc; + pointer root; + + balance_tree(): tree_tag_t(), root{nullptr} {} + ~balance_tree() { + post_order([this](auto it) { alloc.deallocate(it, 1); }); + } + + auto size() const -> tree_size_t { return tree_tag_t::size(root); } + + template + void pre_order(F callback) { + auto f = [&](auto &&f, pointer p) { + if (!p) return; + callback(p), f(f, p->ch[0]), f(f, p->ch[1]); + }; + f(f, root); + } + template + void in_order(F callback) { + auto f = [&](auto &&f, pointer p) { + if (!p) return; + f(f, p->ch[0]), callback(p), f(f, p->ch[1]); + }; + f(f, root); + } + template + void post_order(F callback) { + auto f = [&](auto &&f, pointer p) { + if (!p) return; + f(f, p->ch[0]), f(f, p->ch[1]), callback(p); + }; + f(f, root); + } + + auto leftmost(const_pointer p) const { return most(p, 0); } + auto rightmost(const_pointer p) const { return most(p, 1); } + auto prev(const_pointer p) const { return neighbour(p, 0); } + auto next(const_pointer p) const { return neighbour(p, 1); } + + auto lower_bound(const key_t &key) const -> pointer { + const_pointer now = root, ans = nullptr; + while (now) { + if (!compare(now->data, key)) ans = now, now = now->ch[0]; + else now = now->ch[1]; + } + return (pointer)ans; + } + auto upper_bound(const key_t &key) const -> pointer { + const_pointer now = root, ans = nullptr; + while (now) { + if (compare(key, now->data)) ans = now, now = now->ch[0]; + else now = now->ch[1]; + } + return (pointer)ans; + } + + // Order start from 0 + auto order_of_key(const key_t &key) const -> tree_size_t { + tree_size_t ans = 0; + auto now = root; + while (now) { + if (!compare(now->data, key)) now = now->ch[0]; + else ans += tree_tag_t::size(now->ch[0]) + 1, now = now->ch[1]; + } + return ans; + } + // Order start from 0 + auto find_by_order(tree_size_t order) const -> const_pointer { + const_pointer now = root, ans = nullptr; + while (now && now->sz >= order) { + auto lsize = tree_tag_t::size(now->ch[0]); + if (order < lsize) now = now->ch[0]; + else { + ans = now; + if (order == lsize) break; + now = now->ch[1], order -= lsize + 1; + } + } + return ans; + } + + /** + * @return nullptr if insert failed, otherwise pointer of inserted node + */ + auto insert(const key_t &data) -> const_pointer { + pointer n = alloc.allocate(1); + n->fa = n->ch[0] = n->ch[1] = nullptr; + n->data = data, n->sz = 1; + pointer now = root, p = nullptr; + bool dir = 0; + while (now) { + p = now; + dir = compare(now->data, data); + now = now->ch[dir]; + } + tree_tag_t::insert_leaf(root, p, n, dir); + return n; + } + /** + * @return succeed or not + */ + auto erase(const key_t &key) -> bool { + auto p = lower_bound(key); + if (!p || p->data != key) return false; + erase(p); + return true; + } + /** + * @return {@code next(p)} + */ + auto erase(pointer p) -> const_pointer { + if (!p) return nullptr; + pointer result; + if (p->ch[0] && p->ch[1]) { + auto s = leftmost(p->ch[1]); + std::swap(s->data, p->data); + result = p, p = s; + } else result = next(p); + tree_tag_t::erase_branch_leaf(root, p); + alloc.deallocate(p, 1); + return result; + } + +private: + /** + * @param dir 0: leftmost, 1: rightmost + */ + auto most(const_pointer p, bool dir) const -> pointer { + if (!p) return nullptr; + while (p->ch[dir]) p = p->ch[dir]; + return (pointer)p; + } + /** + * @param dir 0: prev, 1: next + */ + auto neighbour(const_pointer p, bool dir) const -> pointer { + if (!p) return nullptr; + if (p->ch[dir]) return most(p->ch[dir], !dir); + if (p == root) return nullptr; + while (p && p->fa && !p->child_dir()) p = p->fa; + return p ? p->fa : nullptr; + } +}; \ No newline at end of file diff --git a/source/code/zkw-segment-tree-test2/bit.cpp b/source/code/zkw-segment-tree-test/bit.cpp similarity index 100% rename from source/code/zkw-segment-tree-test2/bit.cpp rename to source/code/zkw-segment-tree-test/bit.cpp diff --git a/source/code/zkw-segment-tree-test2/data_gen.cpp b/source/code/zkw-segment-tree-test/data_gen.cpp similarity index 100% rename from source/code/zkw-segment-tree-test2/data_gen.cpp rename to source/code/zkw-segment-tree-test/data_gen.cpp diff --git a/source/code/zkw-segment-tree-test2/seg-r.cpp b/source/code/zkw-segment-tree-test/seg-r.cpp similarity index 100% rename from source/code/zkw-segment-tree-test2/seg-r.cpp rename to source/code/zkw-segment-tree-test/seg-r.cpp diff --git a/source/code/zkw-segment-tree-test2/seg-zkw.cpp b/source/code/zkw-segment-tree-test/seg-zkw.cpp similarity index 99% rename from source/code/zkw-segment-tree-test2/seg-zkw.cpp rename to source/code/zkw-segment-tree-test/seg-zkw.cpp index d130acd1c0..de4d68ef1e 100644 --- a/source/code/zkw-segment-tree-test2/seg-zkw.cpp +++ b/source/code/zkw-segment-tree-test/seg-zkw.cpp @@ -73,8 +73,7 @@ struct node { u32 N = 1; FINLINE void init() { - for (; N <= n + 1; N <<= 1) - ; + for (; N <= n + 1; N <<= 1); for (u32 i = N + 1; i <= N + n; ++i) read(_now(i).sum); for (u32 i = N - 1; i; --i) _now(i).sum = _lch(i).sum + _rch(i).sum; } diff --git a/source/code/zkw-segment-tree-test1/bit.cpp b/source/code/zkw-segment-tree-test1/bit.cpp deleted file mode 100644 index 9967da7bda..0000000000 --- a/source/code/zkw-segment-tree-test1/bit.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include -#include -#include -#include -#include - -#define fp(i, l, r) for (uint64_t i = (l); i <= (r); ++i) -#define lowbit(x) ((x) & -(x)) -#define fr(a) freopen((a), "r", stdin) -#define fw(a) freopen((a), "w", stdout) -#define fc fclose(stdin), fclose(stdout) -#define il inline -#define tpn typename -#define MAXN 10000005 -#define MAXBUF 140000000 - -using u64 = uint64_t; - -il char gc() { - static char buf[MAXBUF], *p1 = buf, *p2 = buf; - return p1 == p2 && - (p2 = (p1 = buf) + fread(buf, 1, MAXBUF, stdin), p1 == p2) ? - EOF : - *p1++; -} -template -il void read(A &x) { - char c; - do { c = getchar(); } while (c < '0' || c > '9'); - x = 0; - do { - x = (x << 3) + (x << 1) + (c ^ 48); - c = getchar(); - } while (c >= '0' && c <= '9'); -} -template -il void read(A &a, B &b) { - read(a); - read(b); -} -template -il void read(A &a, B &b, C &c) { - read(a); - read(b); - read(c); -} - -u64 n, m, c1[MAXN], c2[MAXN], num[MAXN]; -const std::string str1("data"), str3(".in"), str4(".out"); -std::stringstream tmp; -std::string str2; -std::ofstream fout("test3.txt"); -struct timeval start, end; - -void il add(u64 *r, u64 pos, const u64 &v) { - for (; pos <= n; pos += lowbit(pos)) r[pos] += v; -} -u64 il query(u64 *r, u64 pos) { - u64 ans(0); - for (; pos; pos -= lowbit(pos)) ans += r[pos]; - return ans; -} -int main() { - fp(j, 1, 16) { - tmp.clear(); - tmp << j; - tmp >> str2; - std::string file(str1 + str2 + str3); - fr(file.c_str()); - file = str1 + str2 + str4; - fw(file.c_str()); - gettimeofday(&start, NULL); - u64 op, x, y, k, sum1, sum2; - read(n, m); - fp(i, 1, n) { - read(num[i]); - add(c1, i, num[i] - num[i - 1]); - add(c2, i, (i - 1) * (num[i] - num[i - 1])); - } - while (m--) { - read(op, x, y); - if (op & 1) { - read(k); - add(c1, x, k); - add(c1, y + 1, -k); - add(c2, x, k * (x - 1)); - add(c2, y + 1, -k * y); - } else { - sum1 = (x - 1) * query(c1, x - 1) - query(c2, x - 1); - sum2 = y * query(c1, y) - query(c2, y); - printf("%llu\n", sum2 - sum1); - } - } - gettimeofday(&end, NULL); - fout << "data#" << j << ":" << std::endl; - fout << "\tstart:" << start.tv_sec << "." << start.tv_usec << std::endl; - fout << "\tend:" << end.tv_sec << "." << end.tv_usec << std::endl; - fc; - } - fout.close(); - return 0; -} diff --git a/source/code/zkw-segment-tree-test1/seg-r.cpp b/source/code/zkw-segment-tree-test1/seg-r.cpp deleted file mode 100644 index 0cccc0c965..0000000000 --- a/source/code/zkw-segment-tree-test1/seg-r.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include -#include -#include -#include -#include - -#define fp(i, l, r) for (uint64_t i = (l); i <= (r); ++i) -#define fr(a) freopen((a), "r", stdin) -#define fw(a) freopen((a), "w", stdout) -#define fc fclose(stdin), fclose(stdout) -#define il inline -#define ls rt << 1 -#define rs rt << 1 | 1 -#define Mid uint64_t m = ((r - l) >> 1) + l -#define tpn typename -#define MAXN 10000005 -#define MAXBUF 140000000 - -using i64 = int64_t; -using u64 = uint64_t; - -il char gc() { - static char buf[MAXBUF], *p1 = buf, *p2 = buf; - return p1 == p2 && - (p2 = (p1 = buf) + fread(buf, 1, MAXBUF, stdin), p1 == p2) ? - EOF : - *p1++; -} -template -il void read(A &x) { - char c; - do { c = getchar(); } while (c < '0' || c > '9'); - x = 0; - do { - x = (x << 3) + (x << 1) + (c ^ 48); - c = getchar(); - } while (c >= '0' && c <= '9'); -} -template -il void read(A &a, B &b) { - read(a), read(b); -} -template -il void read(A &a, B &b, C &c) { - read(a), read(b), read(c); -} - -u64 sum[MAXN << 2], add[MAXN << 2], a[MAXN]; -const std::string str1("data"), str3(".in"), str4(".out"); -std::stringstream tmp; -std::string str2; -std::ofstream fout("test1.txt"); -struct timeval start, end; - -il void PushUp(const u64 &rt) { sum[rt] = sum[ls] + sum[rs]; } -il void PushDown(const u64 &rt, const u64 &ln, const u64 &rn) { - add[ls] += add[rt]; - sum[ls] += add[rt] * ln; - add[rs] += add[rt]; - sum[rs] += add[rt] * rn; - add[rt] = 0; -} -void Build(const u64 &l, const u64 &r, const u64 &rt) { - if (l == r) { - sum[rt] = a[l]; - return; - } - Mid; - Build(l, m, ls); - Build(m + 1, r, rs); - PushUp(rt); -} -void Update(const u64 &L, - const u64 &R, - const u64 &c, - const u64 &l, - const u64 &r, - const u64 &rt) { - if (L <= l && r <= R) { - sum[rt] += c * (r - l + 1); - add[rt] += c; - return; - } - Mid; - PushDown(rt, m - l + 1, r - m); - if (L <= m) Update(L, R, c, l, m, ls); - if (R > m) Update(L, R, c, m + 1, r, rs); - PushUp(rt); -} -u64 Query( - const u64 &L, const u64 &R, const u64 &l, const u64 &r, const u64 &rt) { - u64 ans = 0; - if (L <= l && r <= R) return sum[rt]; - Mid; - PushDown(rt, m - l + 1, r - m); - if (L <= m) ans += Query(L, R, l, m, ls); - if (R > m) ans += Query(L, R, m + 1, r, rs); - return ans; -} -int main() { - fp(j, 1, 16) { - tmp.clear(); - tmp << j; - tmp >> str2; - std::string file(str1 + str2 + str3); - fr(file.c_str()); - file = str1 + str2 + str4; - fw(file.c_str()); - gettimeofday(&start, NULL); - u64 n = 0, m = 0; - read(n, m); - fp(i, 1, n) read(a[i]); - Build(1, n, 1); - u64 o = 0, x = 0, y = 0, k = 0; - while (m--) { - read(o, x, y); - if (o & 1) { - read(k); - Update(x, y, k, 1, n, 1); - } else printf("%llu\n", Query(x, y, 1, n, 1)); - } - gettimeofday(&end, NULL); - fout << "data#" << j << ":" << std::endl; - fout << "\tstart:" << start.tv_sec << "." << start.tv_usec << std::endl; - fout << "\tend:" << end.tv_sec << "." << end.tv_usec << std::endl; - fc; - } - fout.close(); - return 0; -} diff --git a/source/code/zkw-segment-tree-test1/seg-zkw.cpp b/source/code/zkw-segment-tree-test1/seg-zkw.cpp deleted file mode 100644 index bfef6a2562..0000000000 --- a/source/code/zkw-segment-tree-test1/seg-zkw.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#include -#include -#include - -#define fp(i, l, r) for (uint64_t(i) = (l); (i) <= (r); (i)++) -#define fd(i, l, r) for (uint64_t i = l; i >= r; --i) -#define fr(a) freopen((a), "r", stdin) -#define fw(a) freopen((a), "w", stdout) -#define fc fclose(stdin), fclose(stdout) -#define il inline -#define tpn typename -#define MAXN 10000005 -#define MAXBUF 140000000 - -using u64 = uint64_t; - -il char gc() { - static char buf[MAXBUF], *p1 = buf, *p2 = buf; - return p1 == p2 && - (p2 = (p1 = buf) + fread(buf, 1, MAXBUF, stdin), p1 == p2) ? - EOF : - *p1++; -} -template -il void read(A &x) { - char c; - do { c = getchar(); } while (c < '0' || c > '9'); - x = 0; - do { - x = (x << 3) + (x << 1) + (c ^ 48); - c = getchar(); - } while (c >= '0' && c <= '9'); -} -template -il void read(A &a, B &b) { - read(a), read(b); -} -template -il void read(A &a, B &b, C &c) { - read(a), read(b), read(c); -} - -u64 tree[MAXN << 2], add[MAXN << 2]; -u64 n, N = 1, m; -const std::string str1("data"), str3(".in"), str4(".out"); -std::stringstream tmp; -std::string str2; -std::ofstream fout("test2.txt"); -struct timeval start, end; - -il void build() { - read(n, m); - for (; N <= n + 1; N <<= 1) - ; - fp(i, N + 1, N + n) read(tree[i]); - fd(i, N - 1, 1) tree[i] = tree[i << 1] + tree[i << 1 | 1]; -} -il void update(u64 &s, u64 &t, u64 &k) { - u64 lNum = 0, rNum = 0, nNum = 1; - for (s = N + s - 1, t = N + t + 1; s ^ t ^ 1; s >>= 1, t >>= 1, nNum <<= 1) { - tree[s] += k * lNum; - tree[t] += k * rNum; - if (~s & 1) { - add[s ^ 1] += k; - tree[s ^ 1] += k * nNum; - lNum += nNum; - } - if (t & 1) { - add[t ^ 1] += k; - tree[t ^ 1] += k * nNum; - rNum += nNum; - } - } - for (; s; s >>= 1, t >>= 1) { - tree[s] += k * lNum; - tree[t] += k * rNum; - } -} -il u64 query(u64 &s, u64 &t) { - u64 lNum = 0, rNum = 0, nNum = 1; - u64 ans = 0; - for (s = N + s - 1, t = N + t + 1; s ^ t ^ 1; s >>= 1, t >>= 1, nNum <<= 1) { - if (add[s]) ans += add[s] * lNum; - if (add[t]) ans += add[t] * rNum; - if (~s & 1) { - ans += tree[s ^ 1]; - lNum += nNum; - } - if (t & 1) { - ans += tree[t ^ 1]; - rNum += nNum; - } - } - for (; s; s >>= 1, t >>= 1) { - ans += add[s] * lNum; - ans += add[t] * rNum; - } - return ans; -} -int main() { - fp(j, 1, 16) { - tmp.clear(); - tmp << j; - tmp >> str2; - std::string file(str1 + str2 + str3); - fr(file.c_str()); - file = str1 + str2 + str4; - fw(file.c_str()); - gettimeofday(&start, NULL); - build(); - char c = 0; - u64 x = 0, y = 0, k = 0; - while (m--) { - read(c, x, y); - if (c & 2) printf("%llu\n", query(x, y)); - else { - u64 k; - read(k); - update(x, y, k); - } - } - gettimeofday(&end, NULL); - fout << "data#" << j << ":" << std::endl; - fout << "\tstart:" << start.tv_sec << "." << start.tv_usec << std::endl; - fout << "\tend:" << end.tv_sec << "." << end.tv_usec << std::endl; - fc; - } - fout.close(); - return 0; -}