From 4d155ec792ae50691e7cf0c5bd07fbbc935a5057 Mon Sep 17 00:00:00 2001 From: old-yan Date: Mon, 25 Nov 2024 04:20:30 +0800 Subject: [PATCH] fix splay bug; better window rmq; --- DS/AVL.md | 1 + DS/AssignZkwTree.md | 2 + DS/CatTree.md | 8 +- DS/CompressedTree.md | 4 +- DS/Deque.md | 5 +- DS/ErasableMonoPairHeap.md | 3 + DS/MaskRMQ.md | 12 +- DS/MonoAVL.md | 21 +-- DS/MonoSplay.md | 21 +-- DS/MonoZkwTree.h | 22 ++- DS/MonoZkwTree.md | 23 +-- DS/SegmentBeat.md | 13 +- DS/SparseTable.md | 8 +- DS/Splay.h | 1 + DS/WindowRMQ.h | 74 ---------- DS/WindowRMQ.md | 241 -------------------------------- DS/ZkwTree.md | 12 +- MISC/SlideWindow.h | 107 +++++++++++++- MISC/SlideWindow.md | 154 ++++++++++++++++++-- TEST/local/SlideWindow_test.cpp | 150 ++++++++++++++++++-- TEST/local/WindowRMQ_test.cpp | 107 -------------- TEST/oj/luogu_p10513.cpp | 65 +++++++++ TEST/oj/luogu_p10639.cpp | 55 ++++++++ TEST/oj/luogu_p1440.cpp | 68 +++++++++ TEST/oj/luogu_p1886.cpp | 19 ++- TEST/oj/luogu_p2032.cpp | 95 +++++++++++++ TEST/oj/luogu_p2216.cpp | 53 +++---- TEST/oj/luogu_p3088.cpp | 71 ++++++++++ TEST/oj/luogu_p4513.cpp | 56 ++++++++ TEST/oj/luogu_p6373.cpp | 57 ++++++++ TEST/oj/luogu_p8765.cpp | 94 +++++++++++++ TEST/oj/nc_281740.cpp | 57 ++++++++ TEST/oj/ysp_1243.cpp | 47 +++++++ TEST/oj/ysp_190.cpp | 1 - TEST/oj/ysp_828.cpp | 42 ++++++ TREE/TreeTransfer.md | 2 +- 36 files changed, 1221 insertions(+), 550 deletions(-) delete mode 100644 DS/WindowRMQ.h delete mode 100644 DS/WindowRMQ.md delete mode 100644 TEST/local/WindowRMQ_test.cpp create mode 100644 TEST/oj/luogu_p10513.cpp create mode 100644 TEST/oj/luogu_p10639.cpp create mode 100644 TEST/oj/luogu_p1440.cpp create mode 100644 TEST/oj/luogu_p2032.cpp create mode 100644 TEST/oj/luogu_p3088.cpp create mode 100644 TEST/oj/luogu_p4513.cpp create mode 100644 TEST/oj/luogu_p6373.cpp create mode 100644 TEST/oj/luogu_p8765.cpp create mode 100644 TEST/oj/nc_281740.cpp create mode 100644 TEST/oj/ysp_1243.cpp create mode 100644 TEST/oj/ysp_828.cpp diff --git a/DS/AVL.md b/DS/AVL.md index 23b3f39..54187c3 100644 --- a/DS/AVL.md +++ b/DS/AVL.md @@ -14,6 +14,7 @@ 8. [寿命修改](https://ac.nowcoder.com/acm/problem/275139) 9. [数据结构](https://ac.nowcoder.com/acm/problem/276699) 10. [Dynamic Sequence Range Affine Range Sum](https://judge.yosupo.jp/problem/dynamic_sequence_range_affine_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/242) +11. [Ordered Set](https://judge.yosupo.jp/problem/ordered_set)(https://github.com/yosupo06/library-checker-problems/issues/1243) ### 二、模板功能 diff --git a/DS/AssignZkwTree.md b/DS/AssignZkwTree.md index ac63538..2ea167d 100644 --- a/DS/AssignZkwTree.md +++ b/DS/AssignZkwTree.md @@ -22,6 +22,8 @@ 1. 声明 `value_type` 为值类型。 +​ 在此情况下,由于没有幺元,所以要求在构造的时候必须赋初值。 + ​ 对于进行区间查询,且元素能够快速翻倍的场景,对幺半群的要求: 1. 声明 `value_type` 为值类型; diff --git a/DS/CatTree.md b/DS/CatTree.md index 78eea64..26d88e2 100644 --- a/DS/CatTree.md +++ b/DS/CatTree.md @@ -5,9 +5,11 @@ ​ 练习题目: 1. [3117. 划分数组得到最小的值之和](https://leetcode.cn/problems/minimum-sum-of-values-by-dividing-array/) -2. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) -3. [P1890 gcd区间](https://www.luogu.com.cn/problem/P1890) -4. [P3865 【模板】ST 表](https://www.luogu.com.cn/problem/P3865) +2. [P1440 求m区间内的最小值](https://www.luogu.com.cn/problem/P1440) +3. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) +4. [P1890 gcd区间](https://www.luogu.com.cn/problem/P1890) +5. [P2032 扫描](https://www.luogu.com.cn/problem/P2032) +6. [P3865 【模板】ST 表](https://www.luogu.com.cn/problem/P3865) ### 二、模板功能 diff --git a/DS/CompressedTree.md b/DS/CompressedTree.md index ed544d0..d5f8aff 100644 --- a/DS/CompressedTree.md +++ b/DS/CompressedTree.md @@ -5,7 +5,9 @@ ​ 练习题目: 1. [P1801 黑匣子](https://www.luogu.com.cn/problem/P1801) -2. [U311262 求区间后继](https://www.luogu.com.cn/problem/U311262) +2. [P3088 [USACO13NOV] Crowded Cows S](https://www.luogu.com.cn/problem/P3088) +3. [U311262 求区间后继](https://www.luogu.com.cn/problem/U311262) +4. [Point Set Range Composite (Large Array)](https://judge.yosupo.jp/problem/point_set_range_composite_large_array)(https://github.com/yosupo06/library-checker-problems/issues/828) ### 二、模板功能 diff --git a/DS/Deque.md b/DS/Deque.md index dd1ccd2..b084469 100644 --- a/DS/Deque.md +++ b/DS/Deque.md @@ -5,8 +5,9 @@ ​ 练习题目: 1. [B3656 【模板】双端队列 1](https://www.luogu.com.cn/problem/B3656) -2. [P8496 [NOI2022] 众数](https://www.luogu.com.cn/problem/P8496) -3. [Deque Operate All Composite](https://judge.yosupo.jp/problem/deque_operate_all_composite)(https://github.com/yosupo06/library-checker-problems/issues/815) +2. [P3088 [USACO13NOV] Crowded Cows S](https://www.luogu.com.cn/problem/P3088) +3. [P8496 [NOI2022] 众数](https://www.luogu.com.cn/problem/P8496) +4. [Deque Operate All Composite](https://judge.yosupo.jp/problem/deque_operate_all_composite)(https://github.com/yosupo06/library-checker-problems/issues/815) ### 二、模板功能 diff --git a/DS/ErasableMonoPairHeap.md b/DS/ErasableMonoPairHeap.md index b5618fc..a9e049b 100644 --- a/DS/ErasableMonoPairHeap.md +++ b/DS/ErasableMonoPairHeap.md @@ -5,6 +5,9 @@ ​ 练习题目: 1. [3321. 计算子数组的 x-sum II](https://leetcode.cn/problems/find-x-sum-of-all-k-long-subarrays-ii) +2. [P1440 求m区间内的最小值](https://www.luogu.com.cn/problem/P1440) +3. [P2032 扫描](https://www.luogu.com.cn/problem/P2032) + ### 二、模板功能 diff --git a/DS/MaskRMQ.md b/DS/MaskRMQ.md index 56bed3b..61fcb24 100644 --- a/DS/MaskRMQ.md +++ b/DS/MaskRMQ.md @@ -5,11 +5,13 @@ ​ 练习题目: 1. [Check Corners](http://acm.hdu.edu.cn/showproblem.php?pid=2888) -2. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) -3. [P3793 由乃救爷爷](https://www.luogu.com.cn/problem/P3793) -4. [P3865 【模板】ST 表](https://www.luogu.com.cn/problem/P3865) -5. [k - 路径(hard vension)](https://ac.nowcoder.com/acm/problem/279411) -6. [小红的树上路径查询(hard)](https://ac.nowcoder.com/acm/problem/281352) +2. [P1440 求m区间内的最小值](https://www.luogu.com.cn/problem/P1440) +3. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) +4. [P2032 扫描](https://www.luogu.com.cn/problem/P2032) +5. [P3793 由乃救爷爷](https://www.luogu.com.cn/problem/P3793) +6. [P3865 【模板】ST 表](https://www.luogu.com.cn/problem/P3865) +7. [k - 路径(hard vension)](https://ac.nowcoder.com/acm/problem/279411) +8. [小红的树上路径查询(hard)](https://ac.nowcoder.com/acm/problem/281352) ### 二、模板功能 diff --git a/DS/MonoAVL.md b/DS/MonoAVL.md index 0a46158..267d3d1 100644 --- a/DS/MonoAVL.md +++ b/DS/MonoAVL.md @@ -9,16 +9,17 @@ 2. [P1503 鬼子进村](https://www.luogu.com.cn/problem/P1503) 3. [P1801 黑匣子](https://www.luogu.com.cn/problem/P1801) 4. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) -5. [P3369 【模板】普通平衡树](https://www.luogu.com.cn/problem/P3369) -6. [P3391 【模板】文艺平衡树](https://www.luogu.com.cn/problem/P3391) -7. [P4036 [JSOI2008] 火星人](https://www.luogu.com.cn/problem/P4036) -8. [P4774 [NOI2018] 屠龙勇士](https://www.luogu.com.cn/problem/P4774) -9. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) -10. [U361730 【模板】完全体·堆](https://www.luogu.com.cn/problem/U361730) -11. [翻转排序](https://ac.nowcoder.com/acm/problem/275173) -12. [旅途的终点](https://ac.nowcoder.com/acm/problem/275989) -13. [正义从不打背身](https://ac.nowcoder.com/acm/problem/277862) -14. [Range Reverse Range Sum](https://judge.yosupo.jp/problem/range_reverse_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/538) +5. [P2032 扫描](https://www.luogu.com.cn/problem/P2032) +6. [P3369 【模板】普通平衡树](https://www.luogu.com.cn/problem/P3369) +7. [P3391 【模板】文艺平衡树](https://www.luogu.com.cn/problem/P3391) +8. [P4036 [JSOI2008] 火星人](https://www.luogu.com.cn/problem/P4036) +9. [P4774 [NOI2018] 屠龙勇士](https://www.luogu.com.cn/problem/P4774) +10. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) +11. [U361730 【模板】完全体·堆](https://www.luogu.com.cn/problem/U361730) +12. [翻转排序](https://ac.nowcoder.com/acm/problem/275173) +13. [旅途的终点](https://ac.nowcoder.com/acm/problem/275989) +14. [正义从不打背身](https://ac.nowcoder.com/acm/problem/277862) +15. [Range Reverse Range Sum](https://judge.yosupo.jp/problem/range_reverse_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/538) ### 二、模板功能 diff --git a/DS/MonoSplay.md b/DS/MonoSplay.md index e3d91e8..b68f974 100644 --- a/DS/MonoSplay.md +++ b/DS/MonoSplay.md @@ -8,16 +8,17 @@ 2. [P1503 鬼子进村](https://www.luogu.com.cn/problem/P1503) 3. [P1801 黑匣子](https://www.luogu.com.cn/problem/P1801) 4. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) -5. [P3369 【模板】普通平衡树](https://www.luogu.com.cn/problem/P3369) -6. [P3391 【模板】文艺平衡树](https://www.luogu.com.cn/problem/P3391) -7. [P4036 [JSOI2008] 火星人](https://www.luogu.com.cn/problem/P4036) -8. [P4774 [NOI2018] 屠龙勇士](https://www.luogu.com.cn/problem/P4774) -9. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) -10. [U361730 【模板】完全体·堆](https://www.luogu.com.cn/problem/U361730) -11. [翻转排序](https://ac.nowcoder.com/acm/problem/275173) -12. [旅途的终点](https://ac.nowcoder.com/acm/problem/275989) -13. [正义从不打背身](https://ac.nowcoder.com/acm/problem/277862) -14. [Range Reverse Range Sum](https://judge.yosupo.jp/problem/range_reverse_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/538) +5. [P2032 扫描](https://www.luogu.com.cn/problem/P2032) +6. [P3369 【模板】普通平衡树](https://www.luogu.com.cn/problem/P3369) +7. [P3391 【模板】文艺平衡树](https://www.luogu.com.cn/problem/P3391) +8. [P4036 [JSOI2008] 火星人](https://www.luogu.com.cn/problem/P4036) +9. [P4774 [NOI2018] 屠龙勇士](https://www.luogu.com.cn/problem/P4774) +10. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) +11. [U361730 【模板】完全体·堆](https://www.luogu.com.cn/problem/U361730) +12. [翻转排序](https://ac.nowcoder.com/acm/problem/275173) +13. [旅途的终点](https://ac.nowcoder.com/acm/problem/275989) +14. [正义从不打背身](https://ac.nowcoder.com/acm/problem/277862) +15. [Range Reverse Range Sum](https://judge.yosupo.jp/problem/range_reverse_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/538) ### 二、模板功能 diff --git a/DS/MonoZkwTree.h b/DS/MonoZkwTree.h index 5479a7b..c9957ee 100644 --- a/DS/MonoZkwTree.h +++ b/DS/MonoZkwTree.h @@ -35,6 +35,21 @@ namespace OY { struct FpTransfer { Tp operator()(const Tp &x, const Tp &y) const { return Fp(x, y); } }; +#ifdef __cpp_lib_void_t + template + using void_t = std::void_t; +#else + template + struct make_void { + using type = void; + }; + template + using void_t = typename make_void::type; +#endif + template + struct Has_Not_Equal : std::false_type {}; + template + struct Has_Not_Equal() != std::declval())>> : std::true_type {}; template class Tree { public: @@ -43,7 +58,12 @@ namespace OY { private: size_type m_size, m_cap; std::vector m_sub; - static bool _check_pushup(value_type &old, value_type val) { return old != val ? old = val, true : false; } + static bool _check_pushup(value_type &old, value_type val) { + if constexpr (Has_Not_Equal::value) + return old != val ? old = val, true : false; + else + return old = val, true; + } public: Tree(size_type length = 0) { resize(length); } template diff --git a/DS/MonoZkwTree.md b/DS/MonoZkwTree.md index 444647d..25dbfe3 100644 --- a/DS/MonoZkwTree.md +++ b/DS/MonoZkwTree.md @@ -8,15 +8,20 @@ 2. [Mosaic](https://acm.hdu.edu.cn/showproblem.php?pid=4819) 3. [#130. 树状数组 1 :单点修改,区间查询](https://loj.ac/p/130) 4. [#131. 树状数组 2 :区间修改,单点查询](https://loj.ac/p/131) -5. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) -6. [P1890 gcd区间](https://www.luogu.com.cn/problem/P1890) -7. [P3368 【模板】树状数组 2](https://www.luogu.com.cn/problem/P3368) -8. [P3374 【模板】树状数组 1](https://www.luogu.com.cn/problem/P3374) -9. [P3865 【模板】ST 表](https://www.luogu.com.cn/problem/P3865) -10. [P4094 [HEOI2016/TJOI2016] 字符串](https://www.luogu.com.cn/problem/P4094) -11. [P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并](https://www.luogu.com.cn/problem/P4556) -12. [U311262 求区间后继](https://www.luogu.com.cn/problem/U311262) -13. [Vertex Set Path Composite](https://judge.yosupo.jp/problem/vertex_set_path_composite)(https://github.com/yosupo06/library-checker-problems/issues/190) +5. [P1440 求m区间内的最小值](https://www.luogu.com.cn/problem/P1440) +6. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) +7. [P1890 gcd区间](https://www.luogu.com.cn/problem/P1890) +8. [P2032 扫描](https://www.luogu.com.cn/problem/P2032) +9. [P3368 【模板】树状数组 2](https://www.luogu.com.cn/problem/P3368) +10. [P3374 【模板】树状数组 1](https://www.luogu.com.cn/problem/P3374) +11. [P3865 【模板】ST 表](https://www.luogu.com.cn/problem/P3865) +12. [P4094 [HEOI2016/TJOI2016] 字符串](https://www.luogu.com.cn/problem/P4094) +13. [P4513 小白逛公园](https://www.luogu.com.cn/problem/P4513) +14. [P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并](https://www.luogu.com.cn/problem/P4556) +15. [P6373 「StOI-1」IOI计数](https://www.luogu.com.cn/problem/P6373) +16. [U311262 求区间后继](https://www.luogu.com.cn/problem/U311262) +17. [研究red子序列的红](https://ac.nowcoder.com/acm/problem/281740) +18. [Vertex Set Path Composite](https://judge.yosupo.jp/problem/vertex_set_path_composite)(https://github.com/yosupo06/library-checker-problems/issues/190) ### 二、模板功能 diff --git a/DS/SegmentBeat.md b/DS/SegmentBeat.md index b624ff7..277a246 100644 --- a/DS/SegmentBeat.md +++ b/DS/SegmentBeat.md @@ -13,12 +13,13 @@ 7. [P4314 CPU 监控](https://www.luogu.com.cn/problem/P4314) 8. [P4560 [IOI2014] Wall 砖墙](https://www.luogu.com.cn/problem/P4560) 9. [P6242 【模板】线段树 3](https://www.luogu.com.cn/problem/P6242) -10. [U180387 CTSN loves segment tree](https://www.luogu.com.cn/problem/U180387) -11. [#164. 【清华集训2015】V](https://uoj.ac/problem/164) -12. [#169. 【UR #11】元旦老人与数列](https://uoj.ac/problem/169) -13. [#170. Picks loves segment tree VIII](https://uoj.ac/problem/170) -14. [#228. 基础数据结构练习题](https://uoj.ac/problem/228) -15. [Range Chmin Chmax Add Range Sum](https://judge.yosupo.jp/problem/range_chmin_chmax_add_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/243) +10. [P10639 BZOJ4695 最佳女选手](https://www.luogu.com.cn/problem/P10639) +11. [U180387 CTSN loves segment tree](https://www.luogu.com.cn/problem/U180387) +12. [#164. 【清华集训2015】V](https://uoj.ac/problem/164) +13. [#169. 【UR #11】元旦老人与数列](https://uoj.ac/problem/169) +14. [#170. Picks loves segment tree VIII](https://uoj.ac/problem/170) +15. [#228. 基础数据结构练习题](https://uoj.ac/problem/228) +16. [Range Chmin Chmax Add Range Sum](https://judge.yosupo.jp/problem/range_chmin_chmax_add_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/243) ### 二、模板功能 diff --git a/DS/SparseTable.md b/DS/SparseTable.md index 6a4dcf2..377c0db 100644 --- a/DS/SparseTable.md +++ b/DS/SparseTable.md @@ -4,9 +4,11 @@ ​ 练习题目: -1. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) -2. [P1890 gcd区间](https://www.luogu.com.cn/problem/P1890) -3. [P3865 【模板】ST 表](https://www.luogu.com.cn/problem/P3865) +1. [P1440 求m区间内的最小值](https://www.luogu.com.cn/problem/P1440) +2. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) +3. [P1890 gcd区间](https://www.luogu.com.cn/problem/P1890) +4. [P2032 扫描](https://www.luogu.com.cn/problem/P2032) +5. [P3865 【模板】ST 表](https://www.luogu.com.cn/problem/P3865) ### 二、模板功能 diff --git a/DS/Splay.h b/DS/Splay.h index bb92dab..0321012 100644 --- a/DS/Splay.h +++ b/DS/Splay.h @@ -561,6 +561,7 @@ namespace OY { if (!m_rt) return _ptr(0); state_type state = 0; if (!_lower_bound(key)) return _update_root(), root(); + _update_root(); size_type lc = root()->m_lc; if (!lc) return _ptr(0); if (!_splay_max(&lc)) _rotate_l(&lc); diff --git a/DS/WindowRMQ.h b/DS/WindowRMQ.h deleted file mode 100644 index 4fdfe50..0000000 --- a/DS/WindowRMQ.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -最后修改: -20240416 -测试环境: -gcc11.2,c++11 -clang12.0,C++11 -msvc14.2,C++14 -*/ -#ifndef __OY_WINDOWRMQ__ -#define __OY_WINDOWRMQ__ - -#include -#include -#include -#include - -namespace OY { - namespace WINDOW { - using size_type = uint32_t; - template - struct PointerGetter { - Tp *const m_arr; - Tp operator()(size_type i) const { return m_arr[i]; } - }; - template - struct VectorGetter { - std::vector &m_arr; - Tp operator()(size_type i) const { return m_arr[i]; } - }; - template - class Table { - struct node { - size_type m_index; - Tp m_value; - }; - std::vector m_data; - size_type m_cur, m_window_len, m_ql, m_qr; - Compare m_comp; - Getter m_getter; - public: - Table(size_type length, size_type window_len, Compare comp, Getter getter) : m_cur(-1), m_window_len(window_len), m_ql(0), m_qr(0), m_comp(comp), m_getter(getter) { m_data.resize(length); } - void extend_right() { - Tp val = m_getter(++m_cur); - while (m_ql != m_qr && !m_comp(val, m_data[m_qr - 1].m_value)) m_qr--; - if (m_ql != m_qr && m_data[m_ql].m_index == m_cur - m_window_len) m_ql++; - m_data[m_qr++] = {m_cur, val}; - } - void extend_to(size_type right) { - for (size_type i = 0; i != right + 1; i++) extend_right(); - } - const node *raw_query() const { return &m_data[m_ql]; } - const node *next() { - extend_right(); - return raw_query(); - } - size_type cursor() const { return m_cur; } - bool is_finished() const { return cursor() + 1 == m_data.capacity(); } - }; - } - template - auto make_MaxWindow(WINDOW::size_type length, WINDOW::size_type window_len, Tp *arr) -> WINDOW::Table, WINDOW::PointerGetter> { return WINDOW::Table, WINDOW::PointerGetter>(length, window_len, {}, {arr}); } - template - auto make_MinWindow(WINDOW::size_type length, WINDOW::size_type window_len, Tp *arr) -> WINDOW::Table, WINDOW::PointerGetter> { return WINDOW::Table, WINDOW::PointerGetter>(length, window_len, {}, {arr}); } - template - auto make_MaxWindow(WINDOW::size_type length, WINDOW::size_type window_len, std::vector &arr) -> WINDOW::Table, WINDOW::VectorGetter> { return WINDOW::Table, WINDOW::VectorGetter>(length, window_len, {}, {arr}); } - template - auto make_MinWindow(WINDOW::size_type length, WINDOW::size_type window_len, std::vector &arr) -> WINDOW::Table, WINDOW::VectorGetter> { return WINDOW::Table, WINDOW::VectorGetter>(length, window_len, {}, {arr}); } - template - using MaxWindow = WINDOW::Table, Getter>; - template - using MinWindow = WINDOW::Table, Getter>; -} - -#endif \ No newline at end of file diff --git a/DS/WindowRMQ.md b/DS/WindowRMQ.md deleted file mode 100644 index 82dbcb6..0000000 --- a/DS/WindowRMQ.md +++ /dev/null @@ -1,241 +0,0 @@ -### 一、模板类别 - -​ 数据结构:滑动窗口。 - -​ 练习题目: - -1. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) -2. [P2216 [HAOI2007] 理想的正方形](https://www.luogu.com.cn/problem/P2216) - - - -### 二、模板功能 - - -#### 1.建立滑动窗口 - -1. 数据类型 - - 类型设定 `size_type = uint32_t` ,表示树中下标、区间下标的变量类型。 - - 模板参数 `typename Tp` ,表示元素类型。 - - 模板参数 `typename Compare` ,表示比较函数的类型。 - - 模板参数 `typename Getter` ,表示从区间下标到值的映射函数的类型。 - - 构造参数 `size_type length` ,表示区间长度。 - - 构造参数 `size_type window_len` ,表示维护滑动窗口的长度。 - - 构造参数 `Comp comp` ,表示具体的比较函数。 - - 构造参数 `Getter getter` ,表示从区间下标到值的具体的映射函数。 - -2. 时间复杂度 - - $O(n)$ 。 - -3. 备注 - - 本模板处理的问题为定长子数组的最值查询,当传递 `std::less` 作为比较函数类型时,默认查询区间最大值。 - - 本模板进行查询的区间必须是从左往右的;只能先查询 `[0, k-1]` ,再查询 `[1, k]` ,再往后...而不能打乱。 - - 初始时区间右边界为 `-1` ,表示维护空区间,需要在拓展右边界之后再进行查询。 - - **注意:** - - 本模板不进行参数检查,使用者需要保证各种操作合法。在拓展右边界的操作中,右边界初始为 `-1` ,经过拓展后最远到达 `n - 1` ,之后再拓展则为非法操作;在查询操作中,针对的窗口必须为非空窗口,对空窗口进行查询为非法操作。 - -#### 2.拓展右边界(extend_right) - -1. 数据类型 - -2. 时间复杂度 - - 均摊 $O(1)$ 。 - -3. 备注 - - 本方法使窗口的右边界向右滑动一个位置;由于维护定长滑动窗口,所以左边界也自动向右滑动一个位置。 - - 特别的,如果拓展后的区间长度仍然没有达到指定长度(也就是右边界小于 `m_window_len - 1`),左边界就不会跟着移动。 - -#### 3.拓展右边界到某位置(extend_to) - -1. 数据类型 - - 输入参数 `size_type right` ,表示想令有边界拓展到的位置。 - -2. 时间复杂度 - - 均摊 $O(n)$ ,此处 `n` 指目标位置和当前位置的距离。 - -3. 备注 - - 本方法使窗口的右边界不断向右滑动直到来到 `right` 位置。 - -#### 4.查询区间最值(raw_query) - -1. 数据类型 - - 返回类型 `const node *` ,表示区间最值所在的下标以及其值。 - -2. 时间复杂度 - - $O(1)$ 。 - -3. 备注 - - 本方法表示,在当前维护的窗口内,区间最值所在的下标,及其具体值。 - - 如果窗口内有多个最值,则返回最后出现的下标。 - -#### 5.查询下一个窗口(next) - -1. 数据类型 - - 返回类型 `const node *` ,表示区间最值所在的下标以及其值。 - -2. 时间复杂度 - - 均摊 $O(1)$ 。 - -3. 备注 - - 本方法将右边界向右拓展一位,然后查询当前窗口的最值。 - -#### 6.查询当前窗口的右边界(cursor) - -1. 数据类型 - - 返回类型 `size_type` ,表示当前维护的窗口的右边界。 - -2. 时间复杂度 - - $O(1)$ 。 - -#### 7.查询滑动窗口是否已经滑完(is_finished) - -1. 数据类型 - - 返回类型 `bool` ,表示查询结果。 - -2. 时间复杂度 - - $O(1)$ 。 - -### 三、模板示例 - -```c++ -#include "DS/WindowRMQ.h" -#include "IO/FastIO.h" - -void test_arr_rmq() { - // 一般来说,对静态数组进行查询可以使用 make_ 系列函数 - int arr[9] = {4, 9, 2, 3, 5, 7, 8, 1, 6}; - cout << "arr: "; - for (int i = 0; i < 9; i++) cout << arr[i] << " \n"[i == 8]; - - // 最小值窗口 - auto S_min = OY::make_MinWindow(9, 3, arr); - for (int i = 0; i < 9; i++) { - auto res = S_min.next(); - cout << "min(arr[" << std::max(0, i - 2) << "~" << i << "]) = arr[" << res->m_index << "] " << res->m_value << '\n'; - } - - std::vector arr2{"44", "99", "22", "33", "55", "77", "88", "11", "66"}; - cout << "\narr2: "; - for (int i = 0; i < 9; i++) cout << arr2[i] << " \n"[i == 8]; - // 最大值窗口 - auto S_max = OY::make_MaxWindow(9, 5, arr2); - // 如果不想查询长度太短的窗口,可以先把右边界拓展到长度差一位就够的地方 - S_max.extend_to(3); - for (int i = 4; i < 9; i++) { - auto res = S_max.next(); - cout << "max(arr2[" << std::max(0, i - 4) << "~" << i << "]) = arr2[" << res->m_index << "] " << res->m_value << '\n'; - } - - cout << endl - << endl; -} - -void test_custom_rmq() { - // 一些自定义的序列上的应用 - int a[5][2] = {{3, 159}, - {9, 325}, - {7, 916}, - {8, 287}, - {6, 730}}; - // 对 [3, 9, 7, 8, 6] 建立最大值窗口 - cout << "col1: "; - for (int i = 0; i < 5; i++) cout << a[i][0] << " \n"[i == 4]; - auto getter = [&](int i) { - return a[i][0]; - }; - auto S_max = OY::MaxWindow(5, 3, {}, getter); - S_max.extend_to(1); - for (int i = 2; i < 5; i++) { - auto res = S_max.next(); - cout << "max(col1[" << std::max(0, i - 2) << "~" << i << "]) = col1[" << res->m_index << "] " << res->m_value << '\n'; - } - - // 对 [159, 325, 916, 287, 730] 建立窗口,求十位数最大的数字 - cout << "\ncol2: "; - for (int i = 0; i < 5; i++) cout << a[i][1] << " \n"[i == 4]; - // 自定义比较函数 - auto comp = [](int x, int y) { - return (x % 100 / 10) < (y % 100 / 10); - }; - auto getter2 = [&](int i) { - return a[i][1]; - }; - auto S_max2 = OY::WINDOW::Table(5, 2, comp, getter2); - S_max2.extend_to(0); - for (int i = 1; i < 5; i++) { - auto res = S_max2.next(); - cout << "max(col2[" << std::max(0, i - 1) << "~" << i << "]) = col2[" << res->m_index << "] " << res->m_value << '\n'; - } -} - -int main() { - test_arr_rmq(); - test_custom_rmq(); -} -``` - -``` -#输出如下 -arr: 4 9 2 3 5 7 8 1 6 -min(arr[0~0]) = arr[0] 4 -min(arr[0~1]) = arr[0] 4 -min(arr[0~2]) = arr[2] 2 -min(arr[1~3]) = arr[2] 2 -min(arr[2~4]) = arr[2] 2 -min(arr[3~5]) = arr[3] 3 -min(arr[4~6]) = arr[4] 5 -min(arr[5~7]) = arr[7] 1 -min(arr[6~8]) = arr[7] 1 - -arr2: 44 99 22 33 55 77 88 11 66 -max(arr2[0~4]) = arr2[1] 99 -max(arr2[1~5]) = arr2[1] 99 -max(arr2[2~6]) = arr2[6] 88 -max(arr2[3~7]) = arr2[6] 88 -max(arr2[4~8]) = arr2[6] 88 - - -col1: 3 9 7 8 6 -max(col1[0~2]) = col1[1] 9 -max(col1[1~3]) = col1[1] 9 -max(col1[2~4]) = col1[3] 8 - -col2: 159 325 916 287 730 -max(col2[0~1]) = col2[0] 159 -max(col2[1~2]) = col2[1] 325 -max(col2[2~3]) = col2[3] 287 -max(col2[3~4]) = col2[3] 287 - -``` - diff --git a/DS/ZkwTree.md b/DS/ZkwTree.md index 02250f0..1ccc2d9 100644 --- a/DS/ZkwTree.md +++ b/DS/ZkwTree.md @@ -9,11 +9,13 @@ 3. [P3372 【模板】线段树 1](https://www.luogu.com.cn/problem/P3372) 4. [P3373 【模板】线段树 2](https://www.luogu.com.cn/problem/P3373) 5. [P4560 [IOI2014] Wall 砖墙](https://www.luogu.com.cn/problem/P4560) -6. [U187320 【模板】树状数组 3](https://www.luogu.com.cn/problem/U187320) -7. [U216697 线段树区间历史版本和](https://www.luogu.com.cn/problem/U216697) -8. [#164. 【清华集训2015】V](https://uoj.ac/problem/164) -9. [Range Affine Range Sum](https://judge.yosupo.jp/problem/range_affine_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/233) -10. [Range Affine Point Get](https://judge.yosupo.jp/problem/range_affine_point_get)(https://github.com/yosupo06/library-checker-problems/issues/778) +6. [P8765 [蓝桥杯 2021 国 AB] 翻转括号序列](https://www.luogu.com.cn/problem/P8765) +7. [P10513 括号](https://www.luogu.com.cn/problem/P10513) +8. [U187320 【模板】树状数组 3](https://www.luogu.com.cn/problem/U187320) +9. [U216697 线段树区间历史版本和](https://www.luogu.com.cn/problem/U216697) +10. [#164. 【清华集训2015】V](https://uoj.ac/problem/164) +11. [Range Affine Range Sum](https://judge.yosupo.jp/problem/range_affine_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/233) +12. [Range Affine Point Get](https://judge.yosupo.jp/problem/range_affine_point_get)(https://github.com/yosupo06/library-checker-problems/issues/778) diff --git a/MISC/SlideWindow.h b/MISC/SlideWindow.h index 74e2a98..ae96aa6 100644 --- a/MISC/SlideWindow.h +++ b/MISC/SlideWindow.h @@ -1,6 +1,6 @@ /* 最后修改: -20241013 +20241122 测试环境: gcc11.2,c++14 clang12.0,C++14 @@ -11,23 +11,120 @@ msvc14.2,C++14 #include #include +#include #include #include namespace OY { namespace WINDOW { using size_type = uint32_t; + template + struct node { + size_type m_index; + Tp m_value; + }; + template + struct StaticBuffer { + static node s_buf[MAXN]; + }; + template + node StaticBuffer::s_buf[MAXN]; template - void solve(size_type length, size_type window_len, Callback &&call, ShortenLeft &&left_call, LengthenRight &&right_call) { + void solve(size_type length, size_type window_len, Callback &&call, ShortenLeft &&shorten_call, LengthenRight &&lengthen_call) { size_type l = 0, r = 0; - while (r != window_len) right_call(r++); + while (r != window_len) lengthen_call(r++); call(l, r - 1); while (r != length) { - left_call(l++); - right_call(r++); + shorten_call(l++); + lengthen_call(r++); call(l, r - 1); } } + template + void solve_rmq(size_type length, size_type window_len, Mapping &&mapping, Callback &&call, Compare &&comp) { + using Tp = typename std::decay::type; + auto &&buf = StaticBuffer::s_buf; + size_type ql = 0, qr = 0; + auto right_call = [&](size_type r) { + Tp val = mapping(r); + while (ql != qr && !comp(val, buf[qr - 1].m_value)) qr--; + buf[qr++] = {r, val}; + }; + auto left_call = [&](size_type l) { + if (ql != qr && buf[ql].m_index == l) ql++; + }; + auto call2 = [&](size_type l, size_type r) { call(l, r, buf[ql].m_value); }; + solve(length, window_len, call2, left_call, right_call); + } + template + void solve_max(size_type length, size_type window_len, Mapping &&mapping, Callback &&call) { + using Tp = typename std::decay::type; + solve_rmq(length, window_len, mapping, call, std::less()); + } + template + void solve_min(size_type length, size_type window_len, Mapping &&mapping, Callback &&call) { + using Tp = typename std::decay::type; + solve_rmq(length, window_len, mapping, call, std::greater()); + } + template + struct Iter { + size_type m_window_len, m_l, m_r; + Callback m_call; + ShortenLeft m_shorten; + LengthenRight m_lengthen; + Iter(size_type window_len, Callback call, ShortenLeft shorten, LengthenRight lengthen) : m_window_len(window_len), m_l(-1), m_r(0), m_call(call), m_shorten(shorten), m_lengthen(lengthen) { + while (m_r != m_window_len - 1) lengthen(m_r++); + } + auto next() -> decltype(m_call(0, 0)) { + if (~m_l) m_shorten(m_l); + m_l++; + m_lengthen(m_r); + return m_call(m_l, m_r++); + } + }; + template + struct RMQIter { + struct Deque { + std::vector> m_buf; + size_type m_l, m_r; + Deque(size_type cap) : m_buf(cap), m_l(0), m_r(0) {} + bool empty() const { return m_l == m_r; } + const node &front() const { return m_buf[m_l]; } + const node &back() const { return m_buf[m_r - 1]; } + void pop_front() { m_l++; } + void pop_back() { m_r--; } + void push_back(size_type i, Tp x) { m_buf[m_r++] = {i, x}; } + }; + struct Callback { + const Deque &m_q; + Tp operator()(size_type, size_type) const { return m_q.front().m_value; } + }; + struct Shorten { + Deque &m_q; + Mapping &m_mapping; + void operator()(size_type l) { + if (!m_q.empty() && m_q.front().m_index == l) m_q.pop_front(); + } + }; + struct Lengthen { + Deque &m_q; + Mapping &m_mapping; + void operator()(size_type r) { + Tp val = m_mapping(r); + while (!m_q.empty() && !Compare()(val, m_q.back().m_value)) m_q.pop_back(); + m_q.push_back(r, val); + } + }; + Deque m_q; + Mapping m_mapping; + Iter m_iter; + RMQIter(size_type length, Mapping mapping, size_type window_len) : m_q(length), m_mapping(mapping), m_iter(window_len, {m_q}, {m_q, m_mapping}, {m_q, m_mapping}) {} + Tp next() { return m_iter.next(); } + }; + template + using MaxIter = RMQIter>; + template + using MinIter = RMQIter>; } } diff --git a/MISC/SlideWindow.md b/MISC/SlideWindow.md index cd4e024..021b1f6 100644 --- a/MISC/SlideWindow.md +++ b/MISC/SlideWindow.md @@ -5,6 +5,10 @@ ​ 练习题目: 1. [3321. 计算子数组的 x-sum II](https://leetcode.cn/problems/find-x-sum-of-all-k-long-subarrays-ii) +2. [P1440 求m区间内的最小值](https://www.luogu.com.cn/problem/P1440) +3. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) +4. [P2032 扫描](https://www.luogu.com.cn/problem/P2032) +5. [P2216 [HAOI2007] 理想的正方形](https://www.luogu.com.cn/problem/P2216) ### 二、模板功能 @@ -20,12 +24,12 @@ #include "MISC/SlideWindow.h" #include -void test() { +void test_count_color() { // 窗口数颜色 - cout << "count color count:\n"; - int a[] = {4, 7, 5, 1, 8, 1, 4, 4, 1}; + cout << "count color:\n"; + int a[] = {4, 4, 4, 7, 5, 1, 5, 5, 4, 1, 6}; - int n = 9; + int n = 11; int window_len = 4; std::map mp; auto call = [&](int l, int r) { @@ -42,22 +46,150 @@ void test() { ++cnt; }; OY::WINDOW::solve(n, window_len, call, left_call, right_call); + cout << endl; +} + +void test_max() { + // 窗口最大值 + cout << "query window max:\n"; + int a[] = {4, 4, 4, 7, 5, 1, 5, 5, 4, 1, 6}; + + int n = 11; + int window_len = 4; + auto mapping = [&](int i) { return a[i]; }; + auto call = [&](int l, int r, int val) { + cout << "max of ["; + for (int i = l; i <= r; i++) cout << a[i] << " ]"[i == r]; + cout << " = " << val << endl; + }; + OY::WINDOW::solve_max<100>(n, window_len, mapping, call); + cout << endl; +} + +void test_min() { + // 窗口最小值 + cout << "query window min:\n"; + int a[] = {4, 4, 4, 7, 5, 1, 5, 5, 4, 1, 6}; + + int n = 11; + int window_len = 4; + auto mapping = [&](int i) { return a[i]; }; + auto call = [&](int l, int r, int val) { + cout << "min of ["; + for (int i = l; i <= r; i++) cout << a[i] << " ]"[i == r]; + cout << " = " << val << endl; + }; + OY::WINDOW::solve_min<100>(n, window_len, mapping, call); + cout << endl; +} + +void test_count_color_iter() { + // 窗口颜色数迭代器 + cout << "iter of window color count:\n"; + int a[] = {4, 4, 4, 7, 5, 1, 5, 5, 4, 1, 6}; + + int n = 11; + int window_len = 4; + std::map mp; + auto call = [&](int l, int r) -> std::map & { + return mp; + }; + auto left_call = [&](int i) { + auto &cnt = mp[a[i]]; + if (!--cnt) mp.erase(a[i]); + }; + auto right_call = [&](int i) { + auto &cnt = mp[a[i]]; + ++cnt; + }; + auto iter = OY::WINDOW::Iter(window_len, call, left_call, right_call); + + // 不断调用 iter 获取下一个颜色数 + for (int l = 0, r = window_len; r < n; l++, r++) { + cout << "color count of ["; + for (int i = l; i <= r; i++) cout << a[i] << " ]"[i == r]; + cout << " = " << iter.next().size() << endl; + } + cout << endl; +} + +void test_max_iter() { + // 窗口最大值迭代器 + cout << "iter of window max:\n"; + int a[] = {4, 4, 4, 7, 5, 1, 5, 5, 4, 1, 6}; + + int n = 11; + int window_len = 4; + auto mapping = [&](int i) { return a[i]; }; + auto iter = OY::WINDOW::MaxIter(n, mapping, window_len); + + // 不断调用 iter 获取下一个最大值 + for (int l = 0, r = window_len; r < n; l++, r++) { + cout << "max of ["; + for (int i = l; i <= r; i++) cout << a[i] << " ]"[i == r]; + cout << " = " << iter.next() << endl; + } + cout << endl; } int main() { - test(); + test_count_color(); + test_max(); + test_min(); + test_count_color_iter(); + test_max_iter(); } ``` ``` #输出如下 -count color count: +count color: +color count of [4 4 4 7] = 2 +color count of [4 4 7 5] = 3 color count of [4 7 5 1] = 4 -color count of [7 5 1 8] = 4 -color count of [5 1 8 1] = 3 -color count of [1 8 1 4] = 3 -color count of [8 1 4 4] = 3 -color count of [1 4 4 1] = 2 +color count of [7 5 1 5] = 3 +color count of [5 1 5 5] = 2 +color count of [1 5 5 4] = 3 +color count of [5 5 4 1] = 3 +color count of [5 4 1 6] = 4 + +query window max: +max of [4 4 4 7] = 7 +max of [4 4 7 5] = 7 +max of [4 7 5 1] = 7 +max of [7 5 1 5] = 7 +max of [5 1 5 5] = 5 +max of [1 5 5 4] = 5 +max of [5 5 4 1] = 5 +max of [5 4 1 6] = 6 + +query window min: +min of [4 4 4 7] = 4 +min of [4 4 7 5] = 4 +min of [4 7 5 1] = 1 +min of [7 5 1 5] = 1 +min of [5 1 5 5] = 1 +min of [1 5 5 4] = 1 +min of [5 5 4 1] = 1 +min of [5 4 1 6] = 1 + +iter of window color count: +color count of [4 4 4 7 5] = 2 +color count of [4 4 7 5 1] = 3 +color count of [4 7 5 1 5] = 4 +color count of [7 5 1 5 5] = 3 +color count of [5 1 5 5 4] = 2 +color count of [1 5 5 4 1] = 3 +color count of [5 5 4 1 6] = 3 + +iter of window max: +max of [4 4 4 7 5] = 7 +max of [4 4 7 5 1] = 7 +max of [4 7 5 1 5] = 7 +max of [7 5 1 5 5] = 7 +max of [5 1 5 5 4] = 5 +max of [1 5 5 4 1] = 5 +max of [5 5 4 1 6] = 5 ``` diff --git a/TEST/local/SlideWindow_test.cpp b/TEST/local/SlideWindow_test.cpp index 3e6e29c..4c85ec5 100644 --- a/TEST/local/SlideWindow_test.cpp +++ b/TEST/local/SlideWindow_test.cpp @@ -2,12 +2,12 @@ #include "MISC/SlideWindow.h" #include -void test() { +void test_count_color() { // 窗口数颜色 - cout << "count color count:\n"; - int a[] = {4, 7, 5, 1, 8, 1, 4, 4, 1}; + cout << "count color:\n"; + int a[] = {4, 4, 4, 7, 5, 1, 5, 5, 4, 1, 6}; - int n = 9; + int n = 11; int window_len = 4; std::map mp; auto call = [&](int l, int r) { @@ -24,19 +24,147 @@ void test() { ++cnt; }; OY::WINDOW::solve(n, window_len, call, left_call, right_call); + cout << endl; +} + +void test_max() { + // 窗口最大值 + cout << "query window max:\n"; + int a[] = {4, 4, 4, 7, 5, 1, 5, 5, 4, 1, 6}; + + int n = 11; + int window_len = 4; + auto mapping = [&](int i) { return a[i]; }; + auto call = [&](int l, int r, int val) { + cout << "max of ["; + for (int i = l; i <= r; i++) cout << a[i] << " ]"[i == r]; + cout << " = " << val << endl; + }; + OY::WINDOW::solve_max<100>(n, window_len, mapping, call); + cout << endl; +} + +void test_min() { + // 窗口最小值 + cout << "query window min:\n"; + int a[] = {4, 4, 4, 7, 5, 1, 5, 5, 4, 1, 6}; + + int n = 11; + int window_len = 4; + auto mapping = [&](int i) { return a[i]; }; + auto call = [&](int l, int r, int val) { + cout << "min of ["; + for (int i = l; i <= r; i++) cout << a[i] << " ]"[i == r]; + cout << " = " << val << endl; + }; + OY::WINDOW::solve_min<100>(n, window_len, mapping, call); + cout << endl; +} + +void test_count_color_iter() { + // 窗口颜色数迭代器 + cout << "iter of window color count:\n"; + int a[] = {4, 4, 4, 7, 5, 1, 5, 5, 4, 1, 6}; + + int n = 11; + int window_len = 4; + std::map mp; + auto call = [&](int l, int r) -> std::map & { + return mp; + }; + auto left_call = [&](int i) { + auto &cnt = mp[a[i]]; + if (!--cnt) mp.erase(a[i]); + }; + auto right_call = [&](int i) { + auto &cnt = mp[a[i]]; + ++cnt; + }; + auto iter = OY::WINDOW::Iter(window_len, call, left_call, right_call); + + // 不断调用 iter 获取下一个颜色数 + for (int l = 0, r = window_len; r < n; l++, r++) { + cout << "color count of ["; + for (int i = l; i <= r; i++) cout << a[i] << " ]"[i == r]; + cout << " = " << iter.next().size() << endl; + } + cout << endl; +} + +void test_max_iter() { + // 窗口最大值迭代器 + cout << "iter of window max:\n"; + int a[] = {4, 4, 4, 7, 5, 1, 5, 5, 4, 1, 6}; + + int n = 11; + int window_len = 4; + auto mapping = [&](int i) { return a[i]; }; + auto iter = OY::WINDOW::MaxIter(n, mapping, window_len); + + // 不断调用 iter 获取下一个最大值 + for (int l = 0, r = window_len; r < n; l++, r++) { + cout << "max of ["; + for (int i = l; i <= r; i++) cout << a[i] << " ]"[i == r]; + cout << " = " << iter.next() << endl; + } + cout << endl; } int main() { - test(); + test_count_color(); + test_max(); + test_min(); + test_count_color_iter(); + test_max_iter(); } /* #输出如下 -count color count: +count color: +color count of [4 4 4 7] = 2 +color count of [4 4 7 5] = 3 color count of [4 7 5 1] = 4 -color count of [7 5 1 8] = 4 -color count of [5 1 8 1] = 3 -color count of [1 8 1 4] = 3 -color count of [8 1 4 4] = 3 -color count of [1 4 4 1] = 2 +color count of [7 5 1 5] = 3 +color count of [5 1 5 5] = 2 +color count of [1 5 5 4] = 3 +color count of [5 5 4 1] = 3 +color count of [5 4 1 6] = 4 + +query window max: +max of [4 4 4 7] = 7 +max of [4 4 7 5] = 7 +max of [4 7 5 1] = 7 +max of [7 5 1 5] = 7 +max of [5 1 5 5] = 5 +max of [1 5 5 4] = 5 +max of [5 5 4 1] = 5 +max of [5 4 1 6] = 6 + +query window min: +min of [4 4 4 7] = 4 +min of [4 4 7 5] = 4 +min of [4 7 5 1] = 1 +min of [7 5 1 5] = 1 +min of [5 1 5 5] = 1 +min of [1 5 5 4] = 1 +min of [5 5 4 1] = 1 +min of [5 4 1 6] = 1 + +iter of window color count: +color count of [4 4 4 7 5] = 2 +color count of [4 4 7 5 1] = 3 +color count of [4 7 5 1 5] = 4 +color count of [7 5 1 5 5] = 3 +color count of [5 1 5 5 4] = 2 +color count of [1 5 5 4 1] = 3 +color count of [5 5 4 1 6] = 3 + +iter of window max: +max of [4 4 4 7 5] = 7 +max of [4 4 7 5 1] = 7 +max of [4 7 5 1 5] = 7 +max of [7 5 1 5 5] = 7 +max of [5 1 5 5 4] = 5 +max of [1 5 5 4 1] = 5 +max of [5 5 4 1 6] = 5 */ \ No newline at end of file diff --git a/TEST/local/WindowRMQ_test.cpp b/TEST/local/WindowRMQ_test.cpp deleted file mode 100644 index 61d49d1..0000000 --- a/TEST/local/WindowRMQ_test.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "DS/WindowRMQ.h" -#include "IO/FastIO.h" - -void test_arr_rmq() { - // 一般来说,对静态数组进行查询可以使用 make_ 系列函数 - int arr[9] = {4, 9, 2, 3, 5, 7, 8, 1, 6}; - cout << "arr: "; - for (int i = 0; i < 9; i++) cout << arr[i] << " \n"[i == 8]; - - // 最小值窗口 - auto S_min = OY::make_MinWindow(9, 3, arr); - for (int i = 0; i < 9; i++) { - auto res = S_min.next(); - cout << "min(arr[" << std::max(0, i - 2) << "~" << i << "]) = arr[" << res->m_index << "] " << res->m_value << '\n'; - } - - std::vector arr2{"44", "99", "22", "33", "55", "77", "88", "11", "66"}; - cout << "\narr2: "; - for (int i = 0; i < 9; i++) cout << arr2[i] << " \n"[i == 8]; - // 最大值窗口 - auto S_max = OY::make_MaxWindow(9, 5, arr2); - // 如果不想查询长度太短的窗口,可以先把右边界拓展到长度差一位就够的地方 - S_max.extend_to(3); - for (int i = 4; i < 9; i++) { - auto res = S_max.next(); - cout << "max(arr2[" << std::max(0, i - 4) << "~" << i << "]) = arr2[" << res->m_index << "] " << res->m_value << '\n'; - } - - cout << endl - << endl; -} - -void test_custom_rmq() { - // 一些自定义的序列上的应用 - int a[5][2] = {{3, 159}, - {9, 325}, - {7, 916}, - {8, 287}, - {6, 730}}; - // 对 [3, 9, 7, 8, 6] 建立最大值窗口 - cout << "col1: "; - for (int i = 0; i < 5; i++) cout << a[i][0] << " \n"[i == 4]; - auto getter = [&](int i) { - return a[i][0]; - }; - auto S_max = OY::MaxWindow(5, 3, {}, getter); - S_max.extend_to(1); - for (int i = 2; i < 5; i++) { - auto res = S_max.next(); - cout << "max(col1[" << std::max(0, i - 2) << "~" << i << "]) = col1[" << res->m_index << "] " << res->m_value << '\n'; - } - - // 对 [159, 325, 916, 287, 730] 建立窗口,求十位数最大的数字 - cout << "\ncol2: "; - for (int i = 0; i < 5; i++) cout << a[i][1] << " \n"[i == 4]; - // 自定义比较函数 - auto comp = [](int x, int y) { - return (x % 100 / 10) < (y % 100 / 10); - }; - auto getter2 = [&](int i) { - return a[i][1]; - }; - auto S_max2 = OY::WINDOW::Table(5, 2, comp, getter2); - S_max2.extend_to(0); - for (int i = 1; i < 5; i++) { - auto res = S_max2.next(); - cout << "max(col2[" << std::max(0, i - 1) << "~" << i << "]) = col2[" << res->m_index << "] " << res->m_value << '\n'; - } -} - -int main() { - test_arr_rmq(); - test_custom_rmq(); -} -/* -#输出如下 -arr: 4 9 2 3 5 7 8 1 6 -min(arr[0~0]) = arr[0] 4 -min(arr[0~1]) = arr[0] 4 -min(arr[0~2]) = arr[2] 2 -min(arr[1~3]) = arr[2] 2 -min(arr[2~4]) = arr[2] 2 -min(arr[3~5]) = arr[3] 3 -min(arr[4~6]) = arr[4] 5 -min(arr[5~7]) = arr[7] 1 -min(arr[6~8]) = arr[7] 1 - -arr2: 44 99 22 33 55 77 88 11 66 -max(arr2[0~4]) = arr2[1] 99 -max(arr2[1~5]) = arr2[1] 99 -max(arr2[2~6]) = arr2[6] 88 -max(arr2[3~7]) = arr2[6] 88 -max(arr2[4~8]) = arr2[6] 88 - - -col1: 3 9 7 8 6 -max(col1[0~2]) = col1[1] 9 -max(col1[1~3]) = col1[1] 9 -max(col1[2~4]) = col1[3] 8 - -col2: 159 325 916 287 730 -max(col2[0~1]) = col2[0] 159 -max(col2[1~2]) = col2[1] 325 -max(col2[2~3]) = col2[3] 287 -max(col2[3~4]) = col2[3] 287 - -*/ \ No newline at end of file diff --git a/TEST/oj/luogu_p10513.cpp b/TEST/oj/luogu_p10513.cpp new file mode 100644 index 0000000..f206337 --- /dev/null +++ b/TEST/oj/luogu_p10513.cpp @@ -0,0 +1,65 @@ +#include "DS/ZkwTree.h" +#include "IO/FastIO.h" + +/* +[P10513 括号](https://www.luogu.com.cn/problem/P10513) +*/ +/** + * 本题为最大匹配括号子序列经典题 + * 两个子段合并后的匹配括号数,分为三部分: + * 第一个子段自己的匹配数 + * 第二个子段自己的匹配数 + * 第一个子段未匹配的左括号,和第二个子段未匹配的右括号 + * + * 加上一个区间 flip 修改后,需要同时维护 flip 前和 flip 后的答案,并作切换 + */ + +struct brackets { + uint32_t m_l, m_r, m_sum0, m_sum1; + brackets() = default; + brackets(char c) : m_l(c == '('), m_r(c == ')'), m_sum0(0), m_sum1() {} + brackets(uint32_t l, uint32_t r, uint32_t sum0, uint32_t sum1) : m_l(l), m_r(r), m_sum0(sum0), m_sum1(sum1) {} + void flip() { + std::swap(m_l, m_r); + std::swap(m_sum0, m_sum1); + } + brackets operator+(const brackets &rhs) const { + return {m_l + rhs.m_l, m_r + rhs.m_r, m_sum0 + rhs.m_sum0 + std::min(m_l - m_sum0, rhs.m_r - rhs.m_sum0), m_sum1 + rhs.m_sum1 + std::min(m_r - m_sum1, rhs.m_l - rhs.m_sum1)}; + } +}; +struct Node { + using value_type = brackets; + using modify_type = bool; + using node_type = Node; + static value_type op(const value_type &x, const value_type &y) { return x + y; } + static void map(modify_type modify, node_type *x, auto) { x->m_val.flip(); } + static void com(modify_type modify, node_type *x) { x->m_modify = !x->m_modify; } + value_type m_val; + modify_type m_modify; + const value_type &get() const { return m_val; } + void set(const value_type &val) { m_val = val; } + const modify_type &get_lazy() const { return m_modify; } + bool has_lazy() const { return m_modify; } + void clear_lazy() { m_modify = false; } +}; +void solve_zkw() { + uint32_t n; + std::string s; + cin >> n >> s; + OY::ZKW::Tree S(s.begin(), s.end()); + uint32_t m; + cin >> m; + while (m--) { + char op; + uint32_t l, r; + cin >> op >> l >> r; + if (op == '1') + S.add(l - 1, r - 1, true); + else + cout << S.query(l - 1, r - 1).m_sum0 << endl; + } +} + +int main() { + solve_zkw(); +} \ No newline at end of file diff --git a/TEST/oj/luogu_p10639.cpp b/TEST/oj/luogu_p10639.cpp new file mode 100644 index 0000000..473c423 --- /dev/null +++ b/TEST/oj/luogu_p10639.cpp @@ -0,0 +1,55 @@ +#include "DS/SegmentBeat.h" +#include "IO/FastIO.h" + +/* +[P10639 BZOJ4695 最佳女选手](https://www.luogu.com.cn/problem/P10639) +*/ +/** + * 本题为势能线段树模板题 + */ + +int main() { + uint32_t n; + cin >> n; + auto read = [](auto...) { + int x; + cin >> x; + return x; + }; + OY::ChminChmaxAddTree S(n, read); + using node = decltype(S)::node; + uint32_t m; + cin >> m; + while (m--) { + char op; + cin >> op; + if (op == '1') { + uint32_t l, r; + int x; + cin >> l >> r >> x; + S.add(l - 1, r - 1, node::Add{x}); + } else if (op == '2') { + uint32_t l, r; + int x; + cin >> l >> r >> x; + S.add(l - 1, r - 1, node::Chmax{x}); + } else if (op == '3') { + uint32_t l, r; + int x; + cin >> l >> r >> x; + S.add(l - 1, r - 1, node::Chmin{x}); + } else if (op == '4') { + uint32_t l, r; + cin >> l >> r; + cout << S.query(l - 1, r - 1) << endl; + } else if (op == '5') { + uint32_t l, r; + cin >> l >> r; + cout << S.query(l - 1, r - 1) << endl; + } else { + uint32_t l, r; + cin >> l >> r; + cout << S.query(l - 1, r - 1) << endl; + } + } +} \ No newline at end of file diff --git a/TEST/oj/luogu_p1440.cpp b/TEST/oj/luogu_p1440.cpp new file mode 100644 index 0000000..ff56d8d --- /dev/null +++ b/TEST/oj/luogu_p1440.cpp @@ -0,0 +1,68 @@ +#include "DS/CatTree.h" +#include "DS/ErasableMonoPairHeap.h" +#include "DS/MaskRMQ.h" +#include "DS/MonoZkwTree.h" +#include "DS/SparseTable.h" +#include "IO/FastIO.h" +#include "MISC/SlideWindow.h" + +/* +[P1440 求m区间内的最小值](https://www.luogu.com.cn/problem/P1440) +*/ + +static constexpr uint32_t N = 2000000; +void solve_window() { + uint32_t n, k; + cin >> n >> k; + + auto read = [&](uint32_t i) -> uint32_t { + if (i < k - 1) return -1; + uint32_t x; + cin >> x; + return x; + }; + auto callback = [&](auto, auto, auto val) { cout << val << endl; }; + + cout << "0\n"; + OY::WINDOW::solve_min(n + k - 2, k, read, callback); + cout << endl; +} + +uint32_t arr[N]; +void solve_heap() { + uint32_t n, k; + cin >> n >> k; + for (uint32_t i = 0; i != n; i++) cin >> arr[i]; + OY::ErasableMonoPairHeap> S; + S._reserve(n * 2); + for (uint32_t i = 0; i != n; i++) { + if (i > k) S.erase(arr[i - k - 1]); + cout << S.top() << endl; + S.push(arr[i]); + } +} + +void solve_ds() { + uint32_t n, k; + cin >> n >> k; + auto get_min = [](auto x, auto y) { return x < y ? x : y; }; + auto read = [](auto...) { + uint32_t x; + cin >> x; + return x; + }; + auto S = OY::MaskRMQMinValueTable(n, read); + // auto S = OY::MonoMinTree(n, read); + // auto S = OY::STMinTable(n, read); + // auto S = OY::CatMinTable(n, read); + cout << "0\n"; + for (uint32_t i = 1; i != n; i++) cout << S.query(i >= k ? i - k : 0, i - 1) << endl; +} + +int main() { + solve_window(); + // solve_heap(); + // solve_avl(); + // solve_splay(); + // solve_ds(); +} \ No newline at end of file diff --git a/TEST/oj/luogu_p1886.cpp b/TEST/oj/luogu_p1886.cpp index 6f3322e..e1d344a 100644 --- a/TEST/oj/luogu_p1886.cpp +++ b/TEST/oj/luogu_p1886.cpp @@ -5,8 +5,8 @@ #include "DS/MonoSplay.h" #include "DS/MonoZkwTree.h" #include "DS/SparseTable.h" -#include "DS/WindowRMQ.h" #include "IO/FastIO.h" +#include "MISC/SlideWindow.h" #include @@ -15,23 +15,22 @@ */ static constexpr uint32_t N = 1000000; -int arr[N], Mx[N]; +int arr[N]; void solve_window() { uint32_t n, k; cin >> n >> k; for (uint32_t i = 0; i < n; i++) cin >> arr[i]; - auto S_min = OY::make_MinWindow(n, k, arr); - S_min.extend_to(k - 2); - for (uint32_t i = k - 1; i != n; i++) cout << S_min.next()->m_value << ' '; - cout << endl; + auto mapping = [&](uint32_t i) { return arr[i]; }; + auto callback = [&](uint32_t l, uint32_t r, int val) { cout << val << ' '; }; - auto S_max = OY::make_MaxWindow(n, k, arr); - S_max.extend_to(k - 2); - for (uint32_t i = k - 1; i != n; i++) cout << S_max.next()->m_value << ' '; + OY::WINDOW::solve_min(n, k, mapping, callback); + cout << endl; + OY::WINDOW::solve_max(n, k, mapping, callback); cout << endl; } +int Mx[N]; void solve_heap() { uint32_t n, k; cin >> n >> k; @@ -58,7 +57,6 @@ void solve_avl() { while (r < l + k) S.insert_by_comparator(arr[r++]); cout << S.query(0) << ' '; Mx[l] = S.query(S.size() - 1); - bool zero = false; S.erase_by_comparator(arr[l]); } cout << endl; @@ -76,7 +74,6 @@ void solve_splay() { while (r < l + k) S.insert_by_comparator(arr[r++]); cout << S.query(0) << ' '; Mx[l] = S.query(S.size() - 1); - bool zero = false; S.erase_by_comparator(arr[l]); } cout << endl; diff --git a/TEST/oj/luogu_p2032.cpp b/TEST/oj/luogu_p2032.cpp new file mode 100644 index 0000000..a15a3a3 --- /dev/null +++ b/TEST/oj/luogu_p2032.cpp @@ -0,0 +1,95 @@ +#include "DS/CatTree.h" +#include "DS/ErasableMonoPairHeap.h" +#include "DS/MaskRMQ.h" +#include "DS/MonoAVL.h" +#include "DS/MonoSplay.h" +#include "DS/MonoZkwTree.h" +#include "DS/SparseTable.h" +#include "IO/FastIO.h" +#include "MISC/SlideWindow.h" + +/* +[P2032 扫描](https://www.luogu.com.cn/problem/P2032) +*/ + +static constexpr uint32_t N = 250000; +void solve_window() { + uint32_t n, k; + cin >> n >> k; + + auto read = [](auto...) { + uint32_t x; + cin >> x; + return x; + }; + auto callback = [&](auto, auto, auto val) { cout << val << endl; }; + + OY::WINDOW::solve_max(n, k, read, callback); + cout << endl; +} + +uint32_t arr[N]; +void solve_heap() { + uint32_t n, k; + cin >> n >> k; + for (uint32_t i = 0; i != n; i++) cin >> arr[i]; + OY::ErasableMonoPairHeap S; + S._reserve(n * 2); + for (uint32_t l = 0, r = 0; l + k <= n;) { + while (r < l + k) S.push(arr[r++]); + cout << S.top() << endl; + S.erase(arr[l++]); + } +} + +void solve_avl() { + uint32_t n, k; + cin >> n >> k; + for (uint32_t i = 0; i < n; i++) cin >> arr[i]; + using Tree = OY::MonoAVLSequence; + Tree::_reserve(n + 1); + Tree S; + for (uint32_t l = 0, r = 0; r < n; l++) { + while (r < l + k) S.insert_by_comparator(arr[r++], std::greater<>()); + cout << S.query(0) << endl; + S.erase_by_comparator(arr[l], std::greater<>()); + } +} + +void solve_splay() { + uint32_t n, k; + cin >> n >> k; + for (uint32_t i = 0; i < n; i++) cin >> arr[i]; + using Tree = OY::MonoSplaySequence; + Tree::_reserve(n + 1); + Tree S; + for (uint32_t l = 0, r = 0; r < n; l++) { + while (r < l + k) S.insert_by_comparator(arr[r++], std::greater<>()); + cout << S.query(0) << endl; + S.erase_by_comparator(arr[l], std::greater<>()); + } +} + +void solve_ds() { + uint32_t n, k; + cin >> n >> k; + auto get_max = [](auto x, auto y) { return x > y ? x : y; }; + auto read = [](auto...) { + uint32_t x; + cin >> x; + return x; + }; + auto S = OY::MaskRMQMaxValueTable(n, read); + // auto S = OY::MonoMaxTree(n, read); + // auto S = OY::STMaxTable(n, read); + // auto S = OY::CatMaxTable(n, read); + for (uint32_t l = 0, r = k - 1; r < n; l++, r++) cout << S.query(l, r) << endl; +} + +int main() { + solve_window(); + // solve_heap(); + // solve_avl(); + // solve_splay(); + // solve_ds(); +} \ No newline at end of file diff --git a/TEST/oj/luogu_p2216.cpp b/TEST/oj/luogu_p2216.cpp index bb2f856..4d9162f 100644 --- a/TEST/oj/luogu_p2216.cpp +++ b/TEST/oj/luogu_p2216.cpp @@ -1,7 +1,7 @@ #include "DS/MonoZkwTree2D.h" #include "DS/RMQ2D.h" -#include "DS/WindowRMQ.h" #include "IO/FastIO.h" +#include "MISC/SlideWindow.h" /* [P2216 [HAOI2007] 理想的正方形](https://www.luogu.com.cn/problem/P2216) @@ -12,45 +12,34 @@ */ static constexpr uint32_t N = 1000; -uint32_t m, n, k, val[N][N]; -struct ColValGetter { - const uint32_t col; - uint32_t operator()(uint32_t row) const { return val[row][col]; } -}; +uint32_t val[N][N], Mi[N][N], Mx[N][N]; void solve_window() { + uint32_t m, n, k; cin >> m >> n >> k; for (uint32_t i = 0; i != m; i++) for (uint32_t j = 0; j != n; j++) cin >> val[i][j]; - // 计算每列列内的窗口最小值, ColMin[j].next() 表示第 j 列的每个长 k 的竖条的最小值 - std::vector> ColMin; - ColMin.reserve(n); for (uint32_t j = 0; j != n; j++) { - ColMin.push_back({m, k, {}, {j}}); - ColMin.back().extend_to(k - 2); + auto mapping = [&](uint32_t i) { return val[i][j]; }; + OY::WINDOW::MinIter it(m, mapping, k); + for (uint32_t i = 0; i <= m - k; i++) Mi[i][j] = it.next(); + } + for (uint32_t i = 0; i <= m - k; i++) { + auto mapping = [&](uint32_t j) { return Mi[i][j]; }; + OY::WINDOW::MinIter it(n, mapping, k); + for (uint32_t j = 0; j <= n - k; j++) Mi[i][j] = it.next(); } - // 计算每列列内的窗口最大值, ColMax[j].next() 表示第 j 列的每个长 k 的竖条的最大值 - std::vector> ColMax; - ColMax.reserve(n); for (uint32_t j = 0; j != n; j++) { - ColMax.push_back({m, k, {}, {j}}); - ColMax.back().extend_to(k - 2); + auto mapping = [&](uint32_t i) { return val[i][j]; }; + OY::WINDOW::MaxIter it(m, mapping, k); + for (uint32_t i = 0; i <= m - k; i++) Mx[i][j] = it.next(); } - uint32_t ans = UINT32_MAX; - for (uint32_t i = 0; i != m - k + 1; i++) { - // FrameMin.next() 表示以第 i 行的每个长 k 的横条为最上沿的正方形的最小值 - auto col_min_getter = [&](uint32_t col) { return ColMin[col].next()->m_value; }; - OY::MinWindow FrameMin(n, k, {}, col_min_getter); - FrameMin.extend_to(k - 2); - - // FrameMax.next() 表示以第 i 行的每个长 k 的横条为最上沿的正方形的最大值 - auto col_max_getter = [&](uint32_t col) { return ColMax[col].next()->m_value; }; - OY::MaxWindow FrameMax(n, k, {}, col_max_getter); - FrameMax.extend_to(k - 2); - - for (uint32_t j = k - 1; j != n; j++) ans = std::min(ans, FrameMax.next()->m_value - FrameMin.next()->m_value); + for (uint32_t i = 0; i <= m - k; i++) { + auto mapping = [&](uint32_t j) { return Mx[i][j]; }; + OY::WINDOW::MaxIter it(n, mapping, k); + for (uint32_t j = 0; j <= n - k; j++) ans = std::min(ans, it.next() - Mi[i][j]); } - cout << ans; + cout << ans << "\n^"; } struct MinMax { @@ -61,9 +50,6 @@ struct MinMax { res.m_max = std::max(m_max, rhs.m_max); return res; } - bool operator!=(const MinMax &rhs) const { - return m_min != rhs.m_min || m_max != rhs.m_max; - } }; constexpr MinMax id{1000000000, 0}; void solve_zkw() { @@ -91,6 +77,7 @@ uint32_t mi[N][N]; void solve_rmq2d() { using MaxTree = OY::RMQMaxTable2D; using MinTree = OY::RMQMinTable2D; + uint32_t m, n, k; cin >> m >> n >> k; for (uint32_t i = 0; i != m; i++) for (uint32_t j = 0; j != n; j++) cin >> val[i][j]; diff --git a/TEST/oj/luogu_p3088.cpp b/TEST/oj/luogu_p3088.cpp new file mode 100644 index 0000000..47fe9ca --- /dev/null +++ b/TEST/oj/luogu_p3088.cpp @@ -0,0 +1,71 @@ +#include "DS/CompressedTree.h" +#include "DS/Deque.h" +#include "IO/FastIO.h" + +/* +[P3088 [USACO13NOV] Crowded Cows S](https://www.luogu.com.cn/problem/P3088) +*/ + +static constexpr uint32_t N = 50000; +void solve_window() { + uint64_t n, d; + cin >> n >> d; + + struct Cow { + uint64_t pos, h; + }; + std::vector cows(n); + for (auto &[pos, h] : cows) cin >> pos >> h; + std::ranges::sort(cows, [](auto &x, auto &y) { return x.pos < y.pos; }); + + std::vector bad(n); + OY::GlobalDeque, N * 2> q; + for (uint32_t i = 0; i != n; i++) { + auto [pos, h] = cows[i]; + while (!q.empty() && q.back().h <= h) q.pop_back(); + q.push_back(cows[i]); + while (q.front().pos + d < pos) q.pop_front(); + if (q.front().h >= h * 2) bad[i] = true; + } + + q = OY::GlobalDeque, N * 2>(); + uint32_t ans = 0; + for (uint32_t i = n - 1; ~i; i--) { + auto [pos, h] = cows[i]; + while (!q.empty() && q.back().h <= h) q.pop_back(); + q.push_back(cows[i]); + while (q.front().pos > pos + d) q.pop_front(); + if (q.front().h >= h * 2 && bad[i]) ans++; + } + + cout << ans; +} + +void solve_ds() { + uint32_t n, d; + cin >> n >> d; + + struct Cow { + uint32_t pos, h; + }; + std::vector cows(n); + for (auto &[pos, h] : cows) cin >> pos >> h; + + using Tree = OY::VectorCompressedMaxTree; + Tree::_reserve(200000); + Tree S; + for (auto &[pos, h] : cows) S.add(pos, h); + + uint32_t ans = 0; + for (uint32_t i = 0; i != n; i++) { + auto [pos, h] = cows[i]; + if (S.query(pos >= d ? pos - d : 0, pos - 1) >= h * 2) + if (S.query(pos + 1, pos + d) >= h * 2) ans++; + } + cout << ans; +} + +int main() { + solve_window(); + // solve_ds();s +} \ No newline at end of file diff --git a/TEST/oj/luogu_p4513.cpp b/TEST/oj/luogu_p4513.cpp new file mode 100644 index 0000000..319b69a --- /dev/null +++ b/TEST/oj/luogu_p4513.cpp @@ -0,0 +1,56 @@ +#include "DS/MonoZkwTree.h" +#include "IO/FastIO.h" + +/* +[P4513 小白逛公园](https://www.luogu.com.cn/problem/P4513) +*/ +/** + * 本题为最大子段和经典题 + * 或者说最大子数组和 + * 两个子段合并的最值,要么是两个子段的最值之一,要么是前一段的后缀最大加上后一段的前缀最大 + */ + +void solve_zkw() { + using Tp = int; + static constexpr Tp inf = 0x3f3f3f3f; + struct monoid { + struct value_type { + Tp m_lmx, m_rmx, m_mx, m_sum; + value_type() = default; + value_type(Tp x) : m_lmx(x), m_rmx(x), m_mx(x), m_sum(x) {} + value_type(Tp lmx, Tp rmx, Tp mx, Tp sum) : m_lmx(lmx), m_rmx(rmx), m_mx(mx), m_sum(sum) {} + value_type operator+(const value_type &rhs) const { + return value_type(std::max(m_lmx, m_sum + rhs.m_lmx), std::max(m_rmx + rhs.m_sum, rhs.m_rmx), std::max({m_mx, rhs.m_mx, m_rmx + rhs.m_lmx}), m_sum + rhs.m_sum); + } + }; + static value_type identity() { return {-inf, -inf, -inf, 0}; } + static value_type op(const value_type &x, const value_type &y) { return x + y; } + }; + uint32_t n, m; + cin >> n >> m; + auto read = [](auto...) { + int x; + cin >> x; + return x; + }; + OY::MONOZKW::Tree S(n, read); + while (m--) { + char op; + cin >> op; + if (op == '1') { + uint32_t l, r; + cin >> l >> r; + if (l > r) std::swap(l, r); + cout << S.query(l - 1, r - 1).m_mx << endl; + } else { + uint32_t pos; + int val; + cin >> pos >> val; + S.modify(pos - 1, val); + } + } +} + +int main() { + solve_zkw(); +} \ No newline at end of file diff --git a/TEST/oj/luogu_p6373.cpp b/TEST/oj/luogu_p6373.cpp new file mode 100644 index 0000000..1237a41 --- /dev/null +++ b/TEST/oj/luogu_p6373.cpp @@ -0,0 +1,57 @@ +#include "DS/MonoZkwTree.h" +#include "IO/FastIO.h" + +/* +[P6373 「StOI-1」IOI计数](https://www.luogu.com.cn/problem/P6373) +*/ +/** + * 用矩阵转移实现区间计数特定模式子序列个数 + */ + +struct node { + // 模式串比较特殊,I 出现了两次 + // 所以 pattern[0] 和 pattern[2] 永远一样大 + uint32_t cntI, cntO; + uint64_t cntIO, cntOI, cntIOI; +}; +struct monoid { + using value_type = node; + static value_type identity() { return {}; } + static value_type op(const value_type &x, const value_type &y) { + return { + x.cntI + y.cntI, + x.cntO + y.cntO, + x.cntIO + y.cntIO + uint64_t(x.cntI) * y.cntO, + x.cntOI + y.cntOI + uint64_t(x.cntO) * y.cntI, + x.cntIOI + y.cntIOI + uint64_t(x.cntI) * y.cntOI + uint64_t(x.cntIO) * y.cntI}; + } +}; +void solve_zkw() { + uint32_t n, m; + std::string s; + cin >> n >> m >> s; + OY::MONOZKW::Tree S(n, [&](uint32_t i) { + if (s[i] == 'I') + return node{1, 0}; + else + return node{0, 1}; + }); + while (m--) { + char op; + cin >> op; + if (op == '1') { + uint32_t x; + char c; + cin >> x >> c; + S.modify(x - 1, c == 'I' ? node{1, 0} : node{0, 1}); + } else { + uint32_t l, r; + cin >> l >> r; + cout << S.query(l - 1, r - 1).cntIOI << endl; + } + } +} + +int main() { + solve_zkw(); +} \ No newline at end of file diff --git a/TEST/oj/luogu_p8765.cpp b/TEST/oj/luogu_p8765.cpp new file mode 100644 index 0000000..56aaa9b --- /dev/null +++ b/TEST/oj/luogu_p8765.cpp @@ -0,0 +1,94 @@ +#include "DS/ZkwTree.h" +#include "IO/FastIO.h" + +/* +[P8765 [蓝桥杯 2021 国 AB] 翻转括号序列](https://www.luogu.com.cn/problem/P8765) +*/ +/** + * 按照套路,把左括号赋权 1,右括号赋权 -1 + * 维护前缀和数组 pre + * + * 区间修改,会使得 pre[l~r] 区间内的波形上下翻转,是一个线性变换;pre[r~] 区间后的波形是一个竖直方向的升降 + * + * 给定 l ,求最大 r 使得 s[l~r] 合法。那么,pre[l-1]==pre[r],且要求中间没有比 pre[l-1] 小的 + * 我们先找到 l 右侧第一个比 pre[l-1] 小的地方 end,r 只可能出现在这之前 + * 由于 pre 值是连续变化的,所以其实 end-1 就是答案 + */ + +struct presum { + int m_min, m_max; + presum operator+(const presum &rhs) const { + return {std::min(m_min, rhs.m_min), std::max(m_max, rhs.m_max)}; + } +}; +struct affine { + uint32_t mul = 1, add; + affine operator+(const affine &rhs) const { + return {mul * rhs.mul, add * rhs.mul + rhs.add}; + } +}; +struct Node { + using value_type = presum; + using modify_type = affine; + using node_type = Node; + static value_type op(const value_type &x, const value_type &y) { return x + y; } + static void map(modify_type modify, node_type *x, auto) { + if (modify.mul == -1) std::swap(x->m_val.m_min, x->m_val.m_max), x->m_val.m_min = -x->m_val.m_min, x->m_val.m_max = -x->m_val.m_max; + x->m_val.m_min += modify.add, x->m_val.m_max += modify.add; + } + static void com(modify_type modify, node_type *x) { x->m_modify = x->m_modify + modify; } + value_type m_val; + modify_type m_modify; + const value_type &get() const { return m_val; } + void set(const value_type &val) { m_val = val; } + const modify_type &get_lazy() const { return m_modify; } + void clear_lazy() { m_modify = affine{}; } +}; +void solve_zkw() { + uint32_t n, m; + std::string s; + cin >> n >> m >> s; + OY::ZKW::Tree S(n, [&, cur = 0](uint32_t i) mutable -> presum { + cur += (s[i] == '(') ? 1 : -1; + return {cur, cur}; + }); + auto print = [&] { + for (int i = 0; i < n; i++) cout << S.query(i).m_min << " \n"[i == n - 1]; + }; + while (m--) { + char op; + cin >> op; + if (op == '1') { + uint32_t l, r; + cin >> l >> r, l--, r--; + uint32_t v0 = l ? S.query(l - 1).m_min : 0; + uint32_t v1 = S.query(r).m_min; + S.add(l, r, {0xffffffff, v0 * 2}); + if (r != n - 1) S.add(r + 1, n - 1, {1, v0 * 2 - v1 * 2}); + } else { + uint32_t l; + cin >> l, l--; + auto v0 = l ? S.query(l - 1).m_min : 0; + auto end = S.max_right(l, [v0](auto v) { + return v.m_min >= v0; + }); + if (end == l - 1 || end == l) + cout << "0\n"; + else if (end != n - 1) + cout << end + 1 << endl; + else { + auto r = S.min_left(n - 1, [v0](auto v) { + return v.m_min > v0; + }); + if (r == l) + cout << "0\n"; + else + cout << r << endl; + } + } + } +} + +int main() { + solve_zkw(); +} \ No newline at end of file diff --git a/TEST/oj/nc_281740.cpp b/TEST/oj/nc_281740.cpp new file mode 100644 index 0000000..e364b62 --- /dev/null +++ b/TEST/oj/nc_281740.cpp @@ -0,0 +1,57 @@ +#include "DS/MonoZkwTree.h" +#include "IO/FastIO.h" + +/* +[研究red子序列的红](https://ac.nowcoder.com/acm/problem/281740) +*/ +/** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/96115/F + * 用矩阵转移实现区间计数特定模式子序列个数 + */ + +struct node { + uint32_t cntR, cntE, cntD; + uint64_t cntRE, cntED, cntRED; +}; +struct monoid { + using value_type = node; + static value_type identity() { return {}; } + static value_type op(const value_type &x, const value_type &y) { + return { + x.cntR + y.cntR, + x.cntE + y.cntE, + x.cntD + y.cntD, + x.cntRE + y.cntRE + uint64_t(x.cntR) * y.cntE, + x.cntED + y.cntED + uint64_t(x.cntE) * y.cntD, + x.cntRED + y.cntRED + uint64_t(x.cntR) * y.cntED + uint64_t(x.cntRE) * y.cntD}; + } +}; +void solve_zkw() { + uint32_t n, q; + std::string s1, s2; + cin >> n >> q >> s1 >> s2; + OY::MONOZKW::Tree S(n, [&](uint32_t i) { + if (s1[i] == 'r') return node{1, 0, 0}; + if (s1[i] == 'e') return node{0, 1, 0}; + if (s1[i] == 'd') return node{0, 0, 1}; + return node{}; + }); + OY::MONOZKW::Tree T(n, [&](uint32_t i) { + if (s2[i] == 'r') return node{1, 0, 0}; + if (s2[i] == 'e') return node{0, 1, 0}; + if (s2[i] == 'd') return node{0, 0, 1}; + return node{}; + }); + while (q--) { + uint32_t i; + cin >> i; + auto v1 = S.query(i - 1), v2 = T.query(i - 1); + S.modify(i - 1, v2), T.modify(i - 1, v1); + cout << int64_t(S.query_all().cntRED - T.query_all().cntRED) << endl; + } +} + +int main() { + solve_zkw(); +} \ No newline at end of file diff --git a/TEST/oj/ysp_1243.cpp b/TEST/oj/ysp_1243.cpp new file mode 100644 index 0000000..555aec6 --- /dev/null +++ b/TEST/oj/ysp_1243.cpp @@ -0,0 +1,47 @@ +#include "DS/AVL.h" +#include "DS/StaticBufferWrapWithoutCollect.h" +#include "IO/FastIO.h" + +/* +[Ordered Set](https://judge.yosupo.jp/problem/ordered_set)(https://github.com/yosupo06/library-checker-problems/issues/1243) +*/ +/** + * 本题为平衡树模板题 + * 由于允许离线进行,所以离散化一下,套 01 树状数组最快 + * 标准在线做法是平衡树 + */ + +int main() { + uint32_t n, q; + cin >> n >> q; + auto read = [](auto...) { + uint32_t x; + cin >> x; + return x; + }; + auto S = OY::AVLMultiset, OY::StaticBufferWrapWithoutCollect<1000000>::type>::from_mapping(n, read); + for (uint32_t i = 0; i != q; i++) { + char op; + cin >> op; + if (op == '0') + S.modify_or_insert(read(), [](auto...) {}); + else if (op == '1') + S.erase_by_key(read()); + else if (op == '2') + if (auto k = read(); k > S.size()) + cout << "-1\n"; + else + cout << S.kth(k - 1)->get() << endl; + else if (op == '3') + cout << S.rank(read() + 1) << endl; + else if (op == '4') + if (auto p = S.smaller_bound(read() + 1); p->is_null()) + cout << "-1\n"; + else + cout << p->get() << endl; + else if (auto p = S.lower_bound(read()); p->is_null()) + cout << "-1\n"; + else + cout << p->get() << endl; + } +} diff --git a/TEST/oj/ysp_190.cpp b/TEST/oj/ysp_190.cpp index ad41d64..c13edb6 100644 --- a/TEST/oj/ysp_190.cpp +++ b/TEST/oj/ysp_190.cpp @@ -20,7 +20,6 @@ struct node { node operator+(const node &rhs) const { return node{uint32_t((uint64_t)mul * rhs.mul % P), uint32_t(((uint64_t)add * rhs.mul + rhs.add) % P)}; } - bool operator!=(const node &rhs) const { return mul != rhs.mul || add != rhs.add; } }; node val[N]; constexpr node identity{1, 0}; diff --git a/TEST/oj/ysp_828.cpp b/TEST/oj/ysp_828.cpp new file mode 100644 index 0000000..b5515d7 --- /dev/null +++ b/TEST/oj/ysp_828.cpp @@ -0,0 +1,42 @@ +#include "DS/CompressedTree.h" +#include "IO/FastIO.h" + +/* +[Point Set Range Composite (Large Array)](https://judge.yosupo.jp/problem/point_set_range_composite_large_array)(https://github.com/yosupo06/library-checker-problems/issues/828) +*/ +/** + * 本题为超大区间上,单点更新,区间查询线段树模板题 + */ + +static constexpr uint32_t P = 998244353; +struct node { + uint32_t mul, add; + uint32_t calc(uint64_t i) const { + return (i * mul + add) % P; + } + node operator+(const node &rhs) const { + return node{uint32_t((uint64_t)mul * rhs.mul % P), uint32_t(((uint64_t)add * rhs.mul + rhs.add) % P)}; + } +}; + +constexpr node identity{1, 0}; +int main() { + uint32_t n, q; + cin >> n >> q; + using Tree = OY::CPTREE::Tree>, uint32_t>; + Tree::_reserve(q * 2 + 100); + Tree S; + for (uint32_t i = 0; i != q; i++) { + char op; + cin >> op; + if (op == '0') { + uint32_t p, mul, add; + cin >> p >> mul >> add; + S.modify(p, {mul, add}); + } else { + uint32_t l, r, x; + cin >> l >> r >> x; + cout << S.query(l, r - 1).calc(x) << endl; + } + } +} diff --git a/TREE/TreeTransfer.md b/TREE/TreeTransfer.md index 852ef19..1c915ed 100644 --- a/TREE/TreeTransfer.md +++ b/TREE/TreeTransfer.md @@ -19,7 +19,7 @@ ​ 在整个动态规划过程中,会进行两次 `dfs` 。 -​ 第一次 `dfs` 初始化每个位置的值,并完成从叶到根的信息聚合;此时每个结点的值就包含了对以其为根的子树的统计信息。在第一次 `dfs` 过程中,会使用 `mapping` 函数进行每个点的值得初始化,并使用 `merge` 函数将每个点的值向父结点进行聚合。 +​ 第一次 `dfs` 初始化每个位置的值,并完成从叶到根的信息聚合;此时每个结点的值就包含了对以其为根的子树的统计信息。在第一次 `dfs` 过程中,会使用 `mapping` 函数进行每个点的值的初始化,并使用 `merge` 函数将每个点的值向父结点进行聚合。 ​ 第二次 `dfs` 则是一次单纯的树的遍历,每次从父结点 `p` 走到相应的子结点 `a` 时,已经预先保证父结点 `p` 的信息包含了整棵树的信息。然后我们将 `p` 结点的信息减去 `a` 向 `p` 提供的贡献,把这个值聚合到结点 `a` 的信息里。此时就可以让 `a` 结点的信息也包含整棵树的信息。在这个过程中,需要调用 `exclude` 函数。