diff --git a/DS/AVL.md b/DS/AVL.md index 4c95351..23b3f39 100644 --- a/DS/AVL.md +++ b/DS/AVL.md @@ -11,8 +11,9 @@ 5. [P4070 [SDOI2016] 生成魔咒](https://www.luogu.com.cn/problem/P4070) 6. [P5494 【模板】线段树分裂](https://www.luogu.com.cn/problem/P5494) 7. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) -8. [数据结构](https://ac.nowcoder.com/acm/problem/276699) -9. [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) +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) ### 二、模板功能 diff --git a/DS/AdjDiff.md b/DS/AdjDiff.md index 2b81427..20f29df 100644 --- a/DS/AdjDiff.md +++ b/DS/AdjDiff.md @@ -12,7 +12,8 @@ 6. [P4655 [CEOI2017] Building Bridges](https://www.luogu.com.cn/problem/P4655) 7. [P10843 【MX-J2-T4】Turtle and Cycles](https://www.luogu.com.cn/problem/P10843) 8. [fsl 的背包](https://ac.nowcoder.com/acm/problem/263978) -9. [Static Range Sum](https://judge.yosupo.jp/problem/static_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/398) +9. [k - 路径(hard vension)](https://ac.nowcoder.com/acm/problem/279411) +10. [Static Range Sum](https://judge.yosupo.jp/problem/static_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/398) ### 二、模板功能 diff --git a/DS/BIT01.h b/DS/BIT01.h index cae0a6c..504e598 100644 --- a/DS/BIT01.h +++ b/DS/BIT01.h @@ -71,7 +71,7 @@ namespace OY { return res; } size_type query(size_type i) const { return m_bits[i >> MASK_WIDTH] >> (i & (MASK_SIZE - 1)) & 1; } - size_type query(size_type left, size_type right) const { return presum(right) - presum(left); } + size_type query(size_type left, size_type right) const { return presum(right) - presum(left - 1); } size_type kth(size_type k) const { size_type cursor = -1; for (size_type d = m_icap >> 1; d; d >>= 1) diff --git a/DS/BIT01.md b/DS/BIT01.md index d817aa7..95277e6 100644 --- a/DS/BIT01.md +++ b/DS/BIT01.md @@ -6,6 +6,7 @@ 1. [Hotaru's problem](https://acm.hdu.edu.cn/showproblem.php?pid=5371) 2. [P4070 [SDOI2016] 生成魔咒](https://www.luogu.com.cn/problem/P4070) +3. [寿命修改](https://ac.nowcoder.com/acm/problem/275139) ### 二、模板功能 diff --git a/DS/CompressedTree.md b/DS/CompressedTree.md index e4329b9..ed544d0 100644 --- a/DS/CompressedTree.md +++ b/DS/CompressedTree.md @@ -4,7 +4,8 @@ ​ 练习题目: -1. [U311262 求区间后继](https://www.luogu.com.cn/problem/U311262) +1. [P1801 黑匣子](https://www.luogu.com.cn/problem/P1801) +2. [U311262 求区间后继](https://www.luogu.com.cn/problem/U311262) ### 二、模板功能 diff --git a/DS/ErasableMonoPairHeap.h b/DS/ErasableMonoPairHeap.h new file mode 100644 index 0000000..4160283 --- /dev/null +++ b/DS/ErasableMonoPairHeap.h @@ -0,0 +1,102 @@ +/* +最后修改: +20241020 +测试环境: +gcc11.2,c++11 +clang22.0,C++11 +msvc14.2,C++14 +*/ +#ifndef __OY_ERASABLEMONOPAIRHEAP__ +#define __OY_ERASABLEMONOPAIRHEAP__ + +#include "MonoPairHeap.h" + +namespace OY { + namespace EMONOPH { + using size_type = MONOPH::size_type; + template + struct AddCommutativeGroup { + using value_type = Tp; + static Tp identity() { return Tp{}; } + static Tp op(const Tp &x, const Tp &y) { return x + y; } + static Tp inverse(const Tp &x) { return -x; } + }; + template + struct BitxorCommutativeGroup { + using value_type = Tp; + static Tp identity() { return Tp{}; } + static Tp op(const Tp &x, const Tp &y) { return x ^ y; } + static Tp inverse(const Tp &x) { return x; } + }; + template + struct Trait { + using group = CommutativeGroup; + using value_type = typename group::value_type; + using sum_type = typename MONOPH::Has_Sum_Type::type; + static constexpr bool has_op = MONOPH::Has_Op::value; + }; + template ::has_op> + struct HeapBase : Trait { + typename Trait::sum_type m_prod; + HeapBase() : m_prod(CommutativeGroup::identity()) {} + }; + template + struct HeapBase : Trait {}; + template , template typename BufferType = VectorBufferWithCollect> + class Heap : HeapBase { + public: + using typename HeapBase::group; + using typename HeapBase::value_type; + using typename HeapBase::sum_type; + using HeapBase::has_op; + using heap_type = Heap; + using inner_type = MONOPH::Heap, Compare, BufferType>; + using buffer_type = typename inner_type::buffer_type; + static void _reserve(size_type capacity) { inner_type::_reserve(capacity); } + private: + mutable inner_type m_data, m_lazy; + size_type m_sz; + public: + Heap() : m_sz() {} + void clear() { + m_data.clear(), m_lazy.clear(), m_sz = 0; + if constexpr (has_op) this->m_prod = group::identity(); + } + size_type size() const { return m_sz; } + bool empty() const { return !m_sz; } + void push(const value_type &x) { + m_data.push(x), m_sz++; + if constexpr (has_op) this->m_prod = group::op(this->m_prod, x); + } + void erase(const value_type &x) { + m_lazy.push(x), m_sz--; + if constexpr (has_op) this->m_prod = group::op(this->m_prod, group::inverse(x)); + } + void pop() { + while (!m_lazy.empty() && !Compare()(m_lazy.top(), m_data.top())) m_data.pop(), m_lazy.pop(); + if constexpr (has_op) this->m_prod = group::op(this->m_prod, group::inverse(m_data.top())); + m_data.pop(), m_sz--; + } + const value_type &top() const { + while (!m_lazy.empty() && !Compare()(m_lazy.top(), m_data.top())) m_data.pop(), m_lazy.pop(); + return m_data.top(); + } + void join(heap_type &rhs) { + if (!rhs.empty()) { + m_data.join(rhs.m_data), m_lazy.join(rhs.m_lazy), rhs.m_sz = 0; + if constexpr (has_op) this->m_prod = group::op(this->m_prod, rhs.m_prod), rhs.m_prod = group::identity(); + } + } + void join(heap_type &&rhs) { join(rhs); } + const sum_type &query_all() const { return this->m_prod; } + }; + } + template , template typename BufferType = VectorBufferWithCollect> + using ErasableMonoPairHeap = EMONOPH::Heap, Compare, BufferType>; + template > + using VectorErasableMonoSumPairHeap = EMONOPH::Heap, Compare>; + template > + using VectorErasableMonoBitXorPairHeap = EMONOPH::Heap, Compare>; +} + +#endif \ No newline at end of file diff --git a/DS/ErasableMonoPairHeap.md b/DS/ErasableMonoPairHeap.md new file mode 100644 index 0000000..b5618fc --- /dev/null +++ b/DS/ErasableMonoPairHeap.md @@ -0,0 +1,52 @@ +### 一、模板类别 + +​ 数据结构:可删除元素的配对堆。 + +​ 练习题目: + +1. [3321. 计算子数组的 x-sum II](https://leetcode.cn/problems/find-x-sum-of-all-k-long-subarrays-ii) + + +### 二、模板功能 + +​ 本模板为支持维护信息的堆。同时,借助懒删除技术,实现了堆中元素的删除。 + +​ 与 `MonoPairHeap` 不同的是,本模板要求传入的代数结构为交换群,需要存在逆元。只有通过差分才可以实时维护堆的信息。 + +​ 需要注意的是, `erase` 函数需要保证,每次传入的元素必须仍在堆中,未被删除。 + +​ 在接口上,并无特殊之处。 + + +### 三、模板示例 + +```c++ +#include "DS/ErasableMonoPairHeap.h" +#include "IO/FastIO.h" + +void test() { + OY::VectorErasableMonoSumPairHeap S; + S.push(100); + S.push(400); + S.push(200); + S.push(300); + S.push(500); + S.erase(200); + S.erase(400); + cout << "size = " << S.size() << endl; + S.pop(); + cout << "sum = " << S.query_all() << endl; +} + +int main() { + test(); +} +``` + +``` +#输出如下 +size = 3 +sum = 400 + +``` + diff --git a/DS/GlobalHashBIT.md b/DS/GlobalHashBIT.md index a06b2a8..da34ecd 100644 --- a/DS/GlobalHashBIT.md +++ b/DS/GlobalHashBIT.md @@ -5,10 +5,11 @@ ​ 练习题目: 1. [940. 不同的子序列 II](https://leetcode.cn/problems/distinct-subsequences-ii) -2. [P3369 【模板】普通平衡树](https://www.luogu.com.cn/problem/P3369) -3. [P3372 【模板】线段树 1](https://www.luogu.com.cn/problem/P3372) -4. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) -5. [U187320 【模板】树状数组 3](https://www.luogu.com.cn/problem/U187320) +2. [P1801 黑匣子](https://www.luogu.com.cn/problem/P1801) +3. [P3369 【模板】普通平衡树](https://www.luogu.com.cn/problem/P3369) +4. [P3372 【模板】线段树 1](https://www.luogu.com.cn/problem/P3372) +5. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) +6. [U187320 【模板】树状数组 3](https://www.luogu.com.cn/problem/U187320) diff --git a/DS/MaskRMQ.md b/DS/MaskRMQ.md index 3a9a744..56bed3b 100644 --- a/DS/MaskRMQ.md +++ b/DS/MaskRMQ.md @@ -8,6 +8,8 @@ 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) ### 二、模板功能 diff --git a/DS/MergeSortTree.md b/DS/MergeSortTree.md index 4552af5..bb7e8e3 100644 --- a/DS/MergeSortTree.md +++ b/DS/MergeSortTree.md @@ -5,12 +5,13 @@ ​ 练习题目: 1. [Minimum Sum](https://acm.hdu.edu.cn/showproblem.php?pid=3473) -2. [P3834 【模板】可持久化线段树 2](https://www.luogu.com.cn/problem/P3834) -3. [P4094 [HEOI2016/TJOI2016] 字符串](https://www.luogu.com.cn/problem/P4094) -4. [P10814 【模板】离线二维数点](https://www.luogu.com.cn/problem/P10814) -5. [U311262 求区间后继](https://www.luogu.com.cn/problem/U311262) -6. [fsl 的背包](https://ac.nowcoder.com/acm/problem/263978) -7. [Range Kth Smallest](https://judge.yosupo.jp/problem/range_kth_smallest)(https://github.com/yosupo06/library-checker-problems/issues/310) +2. [P1801 黑匣子](https://www.luogu.com.cn/problem/P1801) +3. [P3834 【模板】可持久化线段树 2](https://www.luogu.com.cn/problem/P3834) +4. [P4094 [HEOI2016/TJOI2016] 字符串](https://www.luogu.com.cn/problem/P4094) +5. [P10814 【模板】离线二维数点](https://www.luogu.com.cn/problem/P10814) +6. [U311262 求区间后继](https://www.luogu.com.cn/problem/U311262) +7. [fsl 的背包](https://ac.nowcoder.com/acm/problem/263978) +8. [Range Kth Smallest](https://judge.yosupo.jp/problem/range_kth_smallest)(https://github.com/yosupo06/library-checker-problems/issues/310) ### 二、模板功能 diff --git a/DS/MonoAVL.md b/DS/MonoAVL.md index 06d6f0d..0a46158 100644 --- a/DS/MonoAVL.md +++ b/DS/MonoAVL.md @@ -7,17 +7,18 @@ 1. [3321. 计算子数组的 x-sum II](https://leetcode.cn/problems/find-x-sum-of-all-k-long-subarrays-ii) 2. [P1503 鬼子进村](https://www.luogu.com.cn/problem/P1503) -3. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) -4. [P3369 【模板】普通平衡树](https://www.luogu.com.cn/problem/P3369) -5. [P3391 【模板】文艺平衡树](https://www.luogu.com.cn/problem/P3391) -6. [P4036 [JSOI2008] 火星人](https://www.luogu.com.cn/problem/P4036) -7. [P4774 [NOI2018] 屠龙勇士](https://www.luogu.com.cn/problem/P4774) -8. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) -9. [U361730 【模板】完全体·堆](https://www.luogu.com.cn/problem/U361730) -10. [翻转排序](https://ac.nowcoder.com/acm/problem/275173) -11. [旅途的终点](https://ac.nowcoder.com/acm/problem/275989) -12. [正义从不打背身](https://ac.nowcoder.com/acm/problem/277862) -13. [Range Reverse Range Sum](https://judge.yosupo.jp/problem/range_reverse_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/538) +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) ### 二、模板功能 diff --git a/DS/MonoPairHeap.h b/DS/MonoPairHeap.h index 6b1e108..87b58ef 100644 --- a/DS/MonoPairHeap.h +++ b/DS/MonoPairHeap.h @@ -103,7 +103,6 @@ namespace OY { static_assert(buffer_type::is_vector_buffer, "Only In Vector Mode"); buffer_type::s_buf.reserve(capacity); } - private: size_type m_rt{}; static node *_ptr(size_type cur) { return buffer_type::data() + cur; } @@ -140,7 +139,6 @@ namespace OY { _pushup(p), _pushup(_ptr(a)); return b ? _merge(_merge(x, a), _merges(b)) : _merge(x, a); } - public: Heap() { static Initializer _init; } Heap(const heap_type &rhs) = delete; diff --git a/DS/MonoPairHeap.md b/DS/MonoPairHeap.md index 5a83e4b..a1d77cc 100644 --- a/DS/MonoPairHeap.md +++ b/DS/MonoPairHeap.md @@ -4,9 +4,11 @@ ​ 练习题目: -1. [P3377 【模板】左偏树(可并堆)](https://www.luogu.com.cn/problem/P3377) -2. [P3378 【模板】堆](https://www.luogu.com.cn/problem/P3378) -3. [U361730 【模板】完全体·堆](https://www.luogu.com.cn/problem/U361730) +1. [P1801 黑匣子](https://www.luogu.com.cn/problem/P1801) +2. [P3377 【模板】左偏树(可并堆)](https://www.luogu.com.cn/problem/P3377) +3. [P3378 【模板】堆](https://www.luogu.com.cn/problem/P3378) +4. [U361730 【模板】完全体·堆](https://www.luogu.com.cn/problem/U361730) +5. [旅途的终点](https://ac.nowcoder.com/acm/problem/275989) ### 二、模板功能 diff --git a/DS/MonoSplay.md b/DS/MonoSplay.md index fac0026..e3d91e8 100644 --- a/DS/MonoSplay.md +++ b/DS/MonoSplay.md @@ -6,17 +6,18 @@ 1. [3321. 计算子数组的 x-sum II](https://leetcode.cn/problems/find-x-sum-of-all-k-long-subarrays-ii) 2. [P1503 鬼子进村](https://www.luogu.com.cn/problem/P1503) -3. [P1886 滑动窗口 /【模板】单调队列](https://www.luogu.com.cn/problem/P1886) -4. [P3369 【模板】普通平衡树](https://www.luogu.com.cn/problem/P3369) -5. [P3391 【模板】文艺平衡树](https://www.luogu.com.cn/problem/P3391) -6. [P4036 [JSOI2008] 火星人](https://www.luogu.com.cn/problem/P4036) -7. [P4774 [NOI2018] 屠龙勇士](https://www.luogu.com.cn/problem/P4774) -8. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) -9. [U361730 【模板】完全体·堆](https://www.luogu.com.cn/problem/U361730) -10. [翻转排序](https://ac.nowcoder.com/acm/problem/275173) -11. [旅途的终点](https://ac.nowcoder.com/acm/problem/275989) -12. [正义从不打背身](https://ac.nowcoder.com/acm/problem/277862) -13. [Range Reverse Range Sum](https://judge.yosupo.jp/problem/range_reverse_range_sum)(https://github.com/yosupo06/library-checker-problems/issues/538) +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) ### 二、模板功能 diff --git a/DS/MultiDimSegTree.h b/DS/MultiDimSegTree.h index 609f507..2e73e6b 100644 --- a/DS/MultiDimSegTree.h +++ b/DS/MultiDimSegTree.h @@ -197,10 +197,10 @@ namespace OY { } }; } - template - using MonoMaxMDSeg = MDSEG::Tree; - template - using MonoMinMDSeg = MDSEG::Tree; + template + using MonoMaxMDSeg = MDSEG::Tree; + template + using MonoMinMDSeg = MDSEG::Tree; template using MonoSumMDST = MDSEG::Tree>, MDSEG::AdjTable, DIM, false>; template diff --git a/DS/SegCounter.md b/DS/SegCounter.md index d564eae..c92e672 100644 --- a/DS/SegCounter.md +++ b/DS/SegCounter.md @@ -5,20 +5,21 @@ ​ 练习题目: 1. [1707. 与数组中元素的最大异或值](https://leetcode.cn/problems/maximum-xor-with-an-element-from-array/) -2. [P1903 [国家集训队] 数颜色 / 维护队列](https://www.luogu.com.cn/problem/P1903) -3. [P3157 [CQOI2011] 动态逆序对](https://www.luogu.com.cn/problem/P3157) -4. [P3369 【模板】普通平衡树](https://www.luogu.com.cn/problem/P3369) -5. [P3759 [TJOI2017] 不勤劳的图书管理员](https://www.luogu.com.cn/problem/P3759) -6. [P4551 最长异或路径](https://www.luogu.com.cn/problem/P4551) -7. [P5494 【模板】线段树分裂](https://www.luogu.com.cn/problem/P5494) -8. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) -9. [P8496 [NOI2022] 众数](https://www.luogu.com.cn/problem/P8496) -10. [P9233 [蓝桥杯 2023 省 A] 颜色平衡树](https://www.luogu.com.cn/problem/P9233) -11. [U109895 [HDU4825]Xor Sum](https://www.luogu.com.cn/problem/U109895) -12. [U109897 [HDU5536]Chip Factory](https://www.luogu.com.cn/problem/U109897) -13. [U109923 [Codechef REBXOR]Nikitosh and xor](https://www.luogu.com.cn/problem/U109923) -14. [速度即转发](https://ac.nowcoder.com/acm/problem/217863) -15. [parent 树上启发式合并](https://ac.nowcoder.com/acm/problem/274852) +2. [P1801 黑匣子](https://www.luogu.com.cn/problem/P1801) +3. [P1903 [国家集训队] 数颜色 / 维护队列](https://www.luogu.com.cn/problem/P1903) +4. [P3157 [CQOI2011] 动态逆序对](https://www.luogu.com.cn/problem/P3157) +5. [P3369 【模板】普通平衡树](https://www.luogu.com.cn/problem/P3369) +6. [P3759 [TJOI2017] 不勤劳的图书管理员](https://www.luogu.com.cn/problem/P3759) +7. [P4551 最长异或路径](https://www.luogu.com.cn/problem/P4551) +8. [P5494 【模板】线段树分裂](https://www.luogu.com.cn/problem/P5494) +9. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) +10. [P8496 [NOI2022] 众数](https://www.luogu.com.cn/problem/P8496) +11. [P9233 [蓝桥杯 2023 省 A] 颜色平衡树](https://www.luogu.com.cn/problem/P9233) +12. [U109895 [HDU4825]Xor Sum](https://www.luogu.com.cn/problem/U109895) +13. [U109897 [HDU5536]Chip Factory](https://www.luogu.com.cn/problem/U109897) +14. [U109923 [Codechef REBXOR]Nikitosh and xor](https://www.luogu.com.cn/problem/U109923) +15. [速度即转发](https://ac.nowcoder.com/acm/problem/217863) +16. [parent 树上启发式合并](https://ac.nowcoder.com/acm/problem/274852) ### 二、模板功能 diff --git a/DS/Splay.md b/DS/Splay.md index 8401f6f..d245378 100644 --- a/DS/Splay.md +++ b/DS/Splay.md @@ -11,8 +11,9 @@ 5. [P4070 [SDOI2016] 生成魔咒](https://www.luogu.com.cn/problem/P4070) 6. [P5494 【模板】线段树分裂](https://www.luogu.com.cn/problem/P5494) 7. [P6136 【模板】普通平衡树(数据加强版)](https://www.luogu.com.cn/problem/P6136) -8. [数据结构](https://ac.nowcoder.com/acm/problem/276699) -9. [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) +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) ### 二、模板功能 diff --git a/DS/WaveLet.md b/DS/WaveLet.md index f7398d3..39166cc 100644 --- a/DS/WaveLet.md +++ b/DS/WaveLet.md @@ -7,14 +7,15 @@ 1. [Minimum Sum](https://acm.hdu.edu.cn/showproblem.php?pid=3473) 2. [1157. 子数组中占绝大多数的元素](https://leetcode.cn/problems/online-majority-element-in-subarray/) 3. [2935. 找出强数对的最大异或值 II](https://leetcode.cn/problems/maximum-strong-pair-xor-ii/) -4. [P3834 【模板】可持久化线段树 2](https://www.luogu.com.cn/problem/P3834) -5. [P4094 [HEOI2016/TJOI2016] 字符串](https://www.luogu.com.cn/problem/P4094) -6. [P5283 [十二省联考 2019] 异或粽子](https://www.luogu.com.cn/problem/P5283) -7. [P7261 [COCI2009-2010#3] PATULJCI](https://www.luogu.com.cn/problem/P7261) -8. [P10814 【模板】离线二维数点](https://www.luogu.com.cn/problem/P10814) -9. [U311262 求区间后继](https://www.luogu.com.cn/problem/U311262) -10. [fsl 的背包](https://ac.nowcoder.com/acm/problem/263978) -11. [Range Kth Smallest](https://judge.yosupo.jp/problem/range_kth_smallest)(https://github.com/yosupo06/library-checker-problems/issues/310) +4. [P1801 黑匣子](https://www.luogu.com.cn/problem/P1801) +5. [P3834 【模板】可持久化线段树 2](https://www.luogu.com.cn/problem/P3834) +6. [P4094 [HEOI2016/TJOI2016] 字符串](https://www.luogu.com.cn/problem/P4094) +7. [P5283 [十二省联考 2019] 异或粽子](https://www.luogu.com.cn/problem/P5283) +8. [P7261 [COCI2009-2010#3] PATULJCI](https://www.luogu.com.cn/problem/P7261) +9. [P10814 【模板】离线二维数点](https://www.luogu.com.cn/problem/P10814) +10. [U311262 求区间后继](https://www.luogu.com.cn/problem/U311262) +11. [fsl 的背包](https://ac.nowcoder.com/acm/problem/263978) +12. [Range Kth Smallest](https://judge.yosupo.jp/problem/range_kth_smallest)(https://github.com/yosupo06/library-checker-problems/issues/310) ### 二、模板功能 diff --git a/MISC/LazyMap.h b/MISC/LazyMap.h new file mode 100644 index 0000000..8d88ce6 --- /dev/null +++ b/MISC/LazyMap.h @@ -0,0 +1,257 @@ +/* +最后修改: +20241024 +测试环境: +gcc11.2,c++11 +clang12.0,C++11 +msvc14.2,C++14 +*/ +#ifndef __OY_LAZYMAP__ +#define __OY_LAZYMAP__ + +#include +#include +#include +#include + +namespace OY { +#ifndef __OY_LAZYIMP__ +#define __OY_LAZYIMP__ + namespace LAZYIMP { + template + struct AddCommutativeGroup { + using value_type = Tp; + static Tp identity() { return Tp{}; } + static Tp op(const Tp &x, const Tp &y) { return x + y; } + static Tp inverse(const Tp &x) { return -x; } + }; + template + struct BitxorCommutativeGroup { + using value_type = Tp; + static Tp identity() { return Tp{}; } + static Tp op(const Tp &x, const Tp &y) { return x ^ y; } + static Tp inverse(const Tp &x) { return x; } + }; + template + struct LazyType {}; + template + using LazyAdd = LazyType>; + template + using LazyBitXor = LazyType>; + template + struct Trait { + using type = Tp; + static constexpr bool is_table = false; + }; + template + struct Trait> { + using type = typename CommutativeGroup::value_type; + struct pair { + type m_val, m_inv; + }; + static constexpr bool is_table = false; + }; + } +#endif + namespace LAZYMAP { + using namespace LAZYIMP; + template ::is_table> + struct TableBase; + template + struct TableBase, false> { + using key_type = Key; + using mapped_type = typename CommutativeGroup::value_type; + using inner_mapped_type = typename Trait>::pair; + using group = CommutativeGroup; + mapped_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(mapped_type inc) { m_lazy = group::op(m_lazy, inc); } + const Key &_ki2o(const Key &k) const { return k; } + const Key &_ko2i(const Key &k) const { return k; } + void _pushdown(inner_mapped_type &p) const { p.m_val = group::op(p.m_val, group::op(p.m_inv, m_lazy)), p.m_inv = group::inverse(m_lazy); } + template + It _reduced(It it) const { return _pushdown(it->second), it; } + inner_mapped_type _make_mapped(const mapped_type &m) const { return {m, group::inverse(m_lazy)}; } + static mapped_type &_get(inner_mapped_type &p) { return p.m_val; } + }; + template + struct TableBase, Mapped, false> { + using key_type = typename CommutativeGroup::value_type; + using mapped_type = Mapped; + using inner_mapped_type = Mapped; + using group = CommutativeGroup; + key_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(key_type inc) { m_lazy = group::op(m_lazy, inc); } + key_type _ki2o(const key_type &k) const { return group::op(k, m_lazy); } + key_type _ko2i(const key_type &k) const { return group::op(k, group::inverse(m_lazy)); } + static void _pushdown(inner_mapped_type &) {} + template + static It _reduced(It it) { return it; } + static const Mapped &_make_mapped(const Mapped &m) { return m; } + static Mapped &_get(Mapped &m) { return m; } + }; + template + struct TableBase, LazyType, false> { + using key_type = typename CommutativeGroup1::value_type; + using mapped_type = typename CommutativeGroup2::value_type; + using inner_mapped_type = typename Trait>::pair; + struct group { + struct value_type { + key_type m_key_lazy; + mapped_type m_mapped_lazy; + }; + static value_type identity() { return {CommutativeGroup1::identity(), CommutativeGroup2::identity()}; } + static value_type op(const value_type &x, const value_type &y) { return {CommutativeGroup1::op(x.m_key_lazy, y.m_key_lazy), CommutativeGroup2::op(x.m_mapped_lazy, y.m_mapped_lazy)}; } + static value_type inverse(const value_type &x) { return {CommutativeGroup1::inverse(x.m_key_lazy), CommutativeGroup2::inverse(x.m_mapped_lazy)}; } + }; + typename group::value_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(typename group::value_type inc) { m_lazy = group::op(m_lazy, inc); } + key_type _ki2o(const key_type &k) const { return CommutativeGroup1::op(k, m_lazy.m_key_lazy); } + key_type _ko2i(const key_type &k) const { return CommutativeGroup1::op(k, CommutativeGroup1::inverse(m_lazy.m_key_lazy)); } + void _pushdown(inner_mapped_type &p) const { p.m_val = CommutativeGroup2::op(p.m_val, CommutativeGroup2::op(p.m_inv, m_lazy.m_mapped_lazy)), p.m_inv = CommutativeGroup2::inverse(m_lazy.m_mapped_lazy); } + template + It _reduced(It it) const { return _pushdown(it->second), it; } + inner_mapped_type _make_mapped(const mapped_type &m) const { return {m, CommutativeGroup2::inverse(m_lazy.m_mapped_lazy)}; } + static mapped_type &_get(inner_mapped_type &p) { return p.m_val; } + }; + template + struct TableBase { + using key_type = Key; + using mapped_type = Table; + using inner_mapped_type = typename Trait::pair; + using group = typename mapped_type::group; + typename group::value_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(typename group::value_type inc) { m_lazy = group::op(m_lazy, inc); } + const Key &_ki2o(const Key &k) const { return k; } + const Key &_ko2i(const Key &k) const { return k; } + void _pushdown(inner_mapped_type &p) const { p.m_val.globally_add(group::op(p.m_inv, m_lazy)), p.m_inv = group::inverse(m_lazy); } + template + It _reduced(It it) const { return _pushdown(it->second), it; } + inner_mapped_type _make_mapped(mapped_type &&m) const { return {std::move(m), group::inverse(m_lazy)}; } + static mapped_type &_get(inner_mapped_type &p) { return p.m_val; } + }; + template + struct TableBase, Table, true> { + using key_type = typename CommutativeGroup::value_type; + using mapped_type = Table; + using inner_mapped_type = typename Trait::pair; + using sub_group = typename mapped_type::group; + struct group { + struct value_type { + key_type m_key_lazy; + typename sub_group::value_type m_mapped_lazy; + }; + static value_type identity() { return {CommutativeGroup::identity(), sub_group::identity()}; } + static value_type op(const value_type &x, const value_type &y) { return {CommutativeGroup::op(x.m_key_lazy, y.m_key_lazy), sub_group::op(x.m_mapped_lazy, y.m_mapped_lazy)}; } + static value_type inverse(const value_type &x) { return {CommutativeGroup::inverse(x.m_key_lazy), sub_group::inverse(x.m_mapped_lazy)}; } + }; + typename group::value_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(typename group::value_type inc) { m_lazy = group::op(m_lazy, inc); } + key_type _ki2o(const key_type &k) const { return CommutativeGroup::op(k, m_lazy.m_key_lazy); } + key_type _ko2i(const key_type &k) const { return CommutativeGroup::op(k, CommutativeGroup::inverse(m_lazy.m_key_lazy)); } + void _pushdown(inner_mapped_type &p) const { p.m_val.globally_add(sub_group::op(p.m_inv, m_lazy.m_mapped_lazy)), p.m_inv = sub_group::inverse(m_lazy.m_mapped_lazy); } + template + It _reduced(It it) const { return _pushdown(it->second), it; } + inner_mapped_type _make_mapped(mapped_type &&m) const { return {std::move(m), sub_group::inverse(m_lazy.m_mapped_lazy)}; } + static mapped_type &_get(inner_mapped_type &p) { return p.m_val; } + }; + template ::type>> + struct Table : TableBase { + using base = TableBase; + using key_type = typename base::key_type; + using mapped_type = typename base::mapped_type; + using inner_mapped_type = typename base::inner_mapped_type; + using inner_table = std::map; + using iterator = typename inner_table::iterator; + mutable inner_table m_map; + Table() { base::clear(); } + iterator begin() const { + auto it = m_map.begin(); + return it == m_map.end() ? it : base::_reduced(it); + } + iterator end() const { return m_map.end(); } + iterator find(key_type k) const { + auto it = m_map.find(base::_ko2i(k)); + return it == m_map.end() ? it : base::_reduced(it); + } + iterator lower_bound(key_type k) const { + auto it = m_map.lower_bound(base::_ko2i(k)); + return it == m_map.end() ? it : base::_reduced(it); + } + iterator upper_bound(key_type k) const { + auto it = m_map.upper_bound(base::_ko2i(k)); + return it == m_map.end() ? it : base::_reduced(it); + } + bool contains(key_type k) const { return find(k) != m_map.end(); } + bool empty() const { return m_map.empty(); } + auto size() const -> decltype(m_map.size()) { return m_map.size(); } + void clear() { inner_table().swap(m_map), base::clear(); } + auto insert_or_assign(key_type k, mapped_type m) -> decltype(m_map.emplace(k, inner_mapped_type())) { + auto res = m_map.emplace(base::_ko2i(k), base::_make_mapped(std::move(m))); + if (!res.second) base::_pushdown(res.first->second), base::_get(res.first->second) = m; + return res; + } + auto insert_or_ignore(key_type k, mapped_type m) -> decltype(m_map.emplace(k, inner_mapped_type())) { return m_map.emplace(base::_ko2i(k), base::_make_mapped(std::move(m))); } + template + auto insert_or_modify(key_type k, Callback &&call) -> decltype(m_map.emplace(k, inner_mapped_type())) { + auto res = m_map.emplace(base::_ko2i(k), base::_make_mapped({})); + base::_pushdown(res.first->second); + call(base::_get(res.first->second), res.second); + return res; + } + bool erase(key_type k) { return m_map.erase(base::_ko2i(k)); } + const mapped_type &get(key_type k, const mapped_type &_default) const { + auto res = find(k); + return res != m_map.end() ? base::_get(res->second) : _default; + } + const mapped_type &get(key_type k) const { return base::_get(find(k)->second); } + mapped_type &get(key_type k) { + auto res = m_map.emplace(base::_ko2i(k), base::_make_mapped({})); + return base::_get(base::_reduced(res.first)->second); + } + template + void enumerate(Callback &&call) const { + for (auto it = m_map.begin(); it != m_map.end(); ++it) { + base::_pushdown(it->second); + call(base::_ki2o(it->first), base::_get(it->second)); + } + } + }; + template + Ostream &operator<<(Ostream &out, const Table &x) { + out << '{'; + bool started = false; + auto call = [&](const typename LAZYIMP::Trait::type &k, const typename Table::mapped_type &v) { + if (started) + out << ", "; + else + started = true; + out << k << ": " << v; + }; + x.enumerate(call); + return out << '}'; + } + } + namespace LAZYIMP { + template + struct Trait> { + struct pair { + LAZYMAP::Table m_val; + typename LAZYMAP::Table::group::value_type m_inv; + }; + static constexpr bool is_table = true; + }; + } + template ::type>> + using LazyKeyAddMap = LAZYMAP::Table, Mapped, Compare>; + template ::type>> + using LazyMappedAddMap = LAZYMAP::Table, Compare>; + template ::type>> + using LazyKeyAddMappedAddMap = LAZYMAP::Table, LAZYMAP::LazyAdd, Compare>; +} + +#endif \ No newline at end of file diff --git a/MISC/LazyMap.md b/MISC/LazyMap.md new file mode 100644 index 0000000..b485077 --- /dev/null +++ b/MISC/LazyMap.md @@ -0,0 +1,163 @@ +### 一、模板类别 + +​ 数据结构:全局懒修改的 `map` 。 + +​ 练习题目: + +1. [k - 路径(hard vension)](https://ac.nowcoder.com/acm/problem/279411) + + +### 二、模板功能 + +​ 本模板对 `std::map` 进行一个封装,基于差分,对 `key` 和 `mapped` 实现了全局懒修改。 + +​ 本模板对 `Key` 和 `Mapped` ,都可以通过 `typename CommutativeGroup` 封装一个支持懒修改的交换群。交换群须满足以下要求: + +1. 声明 `value_type` 为值类型; + +2. 定义静态函数 `op` ,接受两个 `value_type` 参数,返回它们的聚合值; + +3. 定义静态函数 `identity` ,无输入参数,返回幺元。 + +4. 定义静态函数 `inverse` ,输入参数一个 `value_type` ,返回其逆元。 + +​ 本模板要求运算符满足**结合律**和**交换律**。常见的交换群为加法群和异或群。 + +**注意:** + +​ 不推荐通过迭代器访问 `first` 获取键,因为还需要算上全局懒标记的影响,才是真正的键; + +​ 通过 `find` , `lower_bound` , `upper_bound` 以及插入所获取的迭代器,访问 `second` 获取值是合理的,因为懒标记的影响已更新; + +​ 通过迭代器前移后移获取到的迭代器,访问 `second` 获取值是不合理的,因为懒标记的影响未更新。 + +### 三、模板示例 + +```c++ +#include "IO/FastIO.h" +#include "MISC/LazyMap.h" + +void test_key_add() { + // key_type 可以全局加 + OY::LazyKeyAddMap mp; + mp.insert_or_assign(100, "A"); + mp.insert_or_assign(300, "C"); + mp.globally_add(10); + mp.insert_or_assign(200, "B"); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_mapped_add() { + // mapped_type 可以全局加 + OY::LazyMappedAddMap mp; + mp.insert_or_assign("A", 100); + mp.insert_or_assign("C", 300); + mp.globally_add(10); + mp.insert_or_assign("B", 200); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_key_add_mapped_add() { + // key_type 和 mapped_type 都可以全局加 + OY::LazyKeyAddMappedAddMap mp1; + mp1.insert_or_assign(100, 1000); + mp1.globally_add({20, 0}); + mp1.insert_or_assign(200, 2000); + mp1.globally_add({0, 3}); + + OY::LazyKeyAddMappedAddMap mp2; + mp2.insert_or_assign(400, 4000); + mp2.globally_add({0, 5}); + mp2.insert_or_assign(300, 3000); + mp2.globally_add({40, 0}); + mp2.insert_or_assign(200, 70000); + + // map 合并 + mp2.enumerate([&](int k, int v) { + mp1.insert_or_modify(k, [&](int &old, bool flag) { + if (flag) + old = v; + else + old += v; + }); + }); + mp2.clear(); + + // 看看结果 + cout << mp1 << "\n\n"; +} + +void test_key_xor_mapped_mul() { + // 设计一个 key 可以全局异或, mapped 可以全局乘 的 map + + // key 支持异或很简单,有现成的 + using Key = OY::LAZYMAP::LazyBitXor; + + // mapped 支持乘法需要写一个交换群 + struct monoid { + using value_type = double; + static double identity() { return 1; } + static double op(double x, double y) { return x * y; } + static double inverse(double x) { return 1 / x; } + }; + using Mapped = OY::LAZYMAP::LazyType; + + // 组合成一个 map + using T = OY::LAZYMAP::Table; + + // 试试效果 + T mp; + mp.insert_or_assign(18, 10.1); + mp.insert_or_assign(32, 30.3); + mp.globally_add({4, 1}); + mp.insert_or_assign(25, 0.5); + mp.globally_add({0, 0.1}); + mp.globally_add({12, 1}); + + cout << mp << "\n\n"; +} + +void test_map_map() { + // 二维 map + // key_type 和 mapped_type 都可以全局加 + using Map1 = OY::LazyKeyAddMappedAddMap; + using Map2 = OY::LAZYMAP::Table, Map1>; + Map2 mp; + mp.insert_or_assign(10, {}); + mp.insert_or_assign(20, {}); + mp.globally_add({1}); + mp.get(20).insert_or_assign(200, 2000); + mp.insert_or_assign(30, {}); + mp.globally_add({2, {10, 0}}); + mp.get(10).insert_or_assign(100, 1000); + + cout << mp << "\n\n"; +} + +int main() { + test_key_add(); + test_mapped_add(); + test_key_add_mapped_add(); + test_key_xor_mapped_mul(); + test_map_map(); +} +``` + +``` +#输出如下 +{112: A, 202: B, 312: C} + +{A: 112, B: 202, C: 312} + +{120: 1003, 200: 72003, 340: 3000, 440: 4005} + +{26: 1.010000, 21: 0.050000, 40: 3.030000} + +{10: {100: 1000}, 13: {}, 22: {210: 2000}, 23: {}, 32: {}} + +``` + diff --git a/MISC/LazySet.h b/MISC/LazySet.h new file mode 100644 index 0000000..13bfaef --- /dev/null +++ b/MISC/LazySet.h @@ -0,0 +1,123 @@ +/* +最后修改: +20241026 +测试环境: +gcc11.2,c++11 +clang12.0,C++11 +msvc14.2,C++14 +*/ +#ifndef __OY_LAZYSET__ +#define __OY_LAZYSET__ + +#include +#include +#include +#include + +namespace OY { +#ifndef __OY_LAZYIMP__ +#define __OY_LAZYIMP__ + namespace LAZYIMP { + template + struct AddCommutativeGroup { + using value_type = Tp; + static Tp identity() { return Tp{}; } + static Tp op(const Tp &x, const Tp &y) { return x + y; } + static Tp inverse(const Tp &x) { return -x; } + }; + template + struct BitxorCommutativeGroup { + using value_type = Tp; + static Tp identity() { return Tp{}; } + static Tp op(const Tp &x, const Tp &y) { return x ^ y; } + static Tp inverse(const Tp &x) { return x; } + }; + template + struct LazyType {}; + template + using LazyAdd = LazyType>; + template + using LazyBitXor = LazyType>; + template + struct Trait { + using type = Tp; + static constexpr bool is_table = false; + }; + template + struct Trait> { + using type = typename CommutativeGroup::value_type; + struct pair { + type m_val, m_inv; + }; + static constexpr bool is_table = false; + }; + } +#endif + namespace LAZYSET { + using namespace LAZYIMP; + template + struct TableBase; + template + struct TableBase> { + using key_type = typename CommutativeGroup::value_type; + using group = CommutativeGroup; + key_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(key_type inc) { m_lazy = group::op(m_lazy, inc); } + key_type _ki2o(const key_type &k) const { return group::op(k, m_lazy); } + key_type _ko2i(const key_type &k) const { return group::op(k, group::inverse(m_lazy)); } + }; + template ::type>> + struct Table : TableBase { + using base = TableBase; + using key_type = typename base::key_type; + using inner_table = std::set; + using iterator = typename inner_table::iterator; + mutable inner_table m_set; + Table() { base::clear(); } + iterator begin() const { return m_set.begin(); } + iterator end() const { return m_set.end(); } + iterator find(key_type k) const { return m_set.find(base::_ko2i(k)); } + iterator lower_bound(key_type k) const { return m_set.lower_bound(base::_ko2i(k)); } + iterator upper_bound(key_type k) const { return m_set.upper_bound(base::_ko2i(k)); } + bool contains(key_type k) const { return find(k) != m_set.end(); } + bool empty() const { return m_set.empty(); } + auto size() const -> decltype(m_set.size()) { return m_set.size(); } + void clear() { inner_table().swap(m_set), base::clear(); } + auto insert(key_type k) -> decltype(m_set.emplace(k)) { return m_set.insert(base::_ko2i(k)); } + bool erase(key_type k) { return m_set.erase(base::_ko2i(k)); } + template + void enumerate(Callback &&call) const { + for (auto it = m_set.begin(); it != m_set.end(); ++it) call(base::_ki2o(*it)); + } + }; + template + Ostream &operator<<(Ostream &out, const Table &x) { + out << '{'; + bool started = false; + auto call = [&](const typename LAZYIMP::Trait::type &k) { + if (started) + out << ", "; + else + started = true; + out << k; + }; + x.enumerate(call); + return out << '}'; + } + } + namespace LAZYIMP { + template + struct Trait> { + struct pair { + LAZYSET::Table m_val; + typename LAZYSET::Table::group::value_type m_inv; + }; + static constexpr bool is_table = true; + }; + } + template ::type>> + using LazyKeyAddSet = LAZYSET::Table, Compare>; +} + +#endif \ No newline at end of file diff --git a/MISC/LazySet.md b/MISC/LazySet.md new file mode 100644 index 0000000..702543e --- /dev/null +++ b/MISC/LazySet.md @@ -0,0 +1,84 @@ +### 一、模板类别 + +​ 数据结构:全局懒修改的 `set` 。 + +​ 练习题目: + +1. [P3806 【模板】点分治1](https://www.luogu.com.cn/problem/P3806) + + +### 二、模板功能 + +​ 本模板对 `std::set` 进行一个封装,基于差分,对 `key` 实现了全局懒修改。 + +​ 本模板对 `Key` ,可以通过 `typename CommutativeGroup` 封装一个支持懒修改的交换群。交换群须满足以下要求: + +1. 声明 `value_type` 为值类型; + +2. 定义静态函数 `op` ,接受两个 `value_type` 参数,返回它们的聚合值; + +3. 定义静态函数 `identity` ,无输入参数,返回幺元。 + +4. 定义静态函数 `inverse` ,输入参数一个 `value_type` ,返回其逆元。 + +​ 本模板要求运算符满足**结合律**和**交换律**。常见的交换群为加法群和异或群。 + +**注意:** + +​ 不推荐通过迭代器解引用获取键,因为还需要算上全局懒标记的影响,才是真正的键; + +### 三、模板示例 + +```c++ +#include "IO/FastIO.h" +#include "MISC/LazySet.h" + +void test_key_add() { + // key_type 可以全局加 + OY::LazyKeyAddSet mp; + mp.insert(100); + mp.insert(300); + mp.globally_add(10); + mp.insert(200); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_key_mul() { + // 设计一个 key 可以全局乘 的 set + + // 需要写一个交换群 + struct monoid { + using value_type = double; + static double identity() { return 1; } + static double op(double x, double y) { return x * y; } + static double inverse(double x) { return 1 / x; } + }; + using T = OY::LAZYSET::Table>; + + // 试试效果 + T mp; + mp.insert(10.1); + mp.insert(30.3); + mp.globally_add(4); + mp.insert(25); + mp.globally_add(0.1); + + cout << mp << "\n\n"; +} + +int main() { + test_key_add(); + test_key_mul(); +} +``` + +``` +#输出如下 +{112, 202, 312} + +{2.500000, 4.040000, 12.120000} + +``` + diff --git a/MISC/LazyUmap.h b/MISC/LazyUmap.h new file mode 100644 index 0000000..eab9245 --- /dev/null +++ b/MISC/LazyUmap.h @@ -0,0 +1,249 @@ +/* +最后修改: +20241024 +测试环境: +gcc11.2,c++11 +clang12.0,C++11 +msvc14.2,C++14 +*/ +#ifndef __OY_LAZYUMAP__ +#define __OY_LAZYUMAP__ + +#include +#include +#include +#include + +namespace OY { +#ifndef __OY_LAZYIMP__ +#define __OY_LAZYIMP__ + namespace LAZYIMP { + template + struct AddCommutativeGroup { + using value_type = Tp; + static Tp identity() { return Tp{}; } + static Tp op(const Tp &x, const Tp &y) { return x + y; } + static Tp inverse(const Tp &x) { return -x; } + }; + template + struct BitxorCommutativeGroup { + using value_type = Tp; + static Tp identity() { return Tp{}; } + static Tp op(const Tp &x, const Tp &y) { return x ^ y; } + static Tp inverse(const Tp &x) { return x; } + }; + template + struct LazyType {}; + template + using LazyAdd = LazyType>; + template + using LazyBitXor = LazyType>; + template + struct Trait { + using type = Tp; + static constexpr bool is_table = false; + }; + template + struct Trait> { + using type = typename CommutativeGroup::value_type; + struct pair { + type m_val, m_inv; + }; + static constexpr bool is_table = false; + }; + } +#endif + namespace LAZYUMAP { + using namespace LAZYIMP; + template ::is_table> + struct TableBase; + template + struct TableBase, false> { + using key_type = Key; + using mapped_type = typename CommutativeGroup::value_type; + using inner_mapped_type = typename Trait>::pair; + using group = CommutativeGroup; + mapped_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(mapped_type inc) { m_lazy = group::op(m_lazy, inc); } + const Key &_ki2o(const Key &k) const { return k; } + const Key &_ko2i(const Key &k) const { return k; } + void _pushdown(inner_mapped_type &p) const { p.m_val = group::op(p.m_val, group::op(p.m_inv, m_lazy)), p.m_inv = group::inverse(m_lazy); } + template + It _reduced(It it) const { return _pushdown(it->second), it; } + inner_mapped_type _make_mapped(const mapped_type &m) const { return {m, group::inverse(m_lazy)}; } + static mapped_type &_get(inner_mapped_type &p) { return p.m_val; } + }; + template + struct TableBase, Mapped, false> { + using key_type = typename CommutativeGroup::value_type; + using mapped_type = Mapped; + using inner_mapped_type = Mapped; + using group = CommutativeGroup; + key_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(key_type inc) { m_lazy = group::op(m_lazy, inc); } + key_type _ki2o(const key_type &k) const { return group::op(k, m_lazy); } + key_type _ko2i(const key_type &k) const { return group::op(k, group::inverse(m_lazy)); } + static void _pushdown(inner_mapped_type &) {} + template + static It _reduced(It it) { return it; } + static const Mapped &_make_mapped(const Mapped &m) { return m; } + static Mapped &_get(Mapped &m) { return m; } + }; + template + struct TableBase, LazyType, false> { + using key_type = typename CommutativeGroup1::value_type; + using mapped_type = typename CommutativeGroup2::value_type; + using inner_mapped_type = typename Trait>::pair; + struct group { + struct value_type { + key_type m_key_lazy; + mapped_type m_mapped_lazy; + }; + static value_type identity() { return {CommutativeGroup1::identity(), CommutativeGroup2::identity()}; } + static value_type op(const value_type &x, const value_type &y) { return {CommutativeGroup1::op(x.m_key_lazy, y.m_key_lazy), CommutativeGroup2::op(x.m_mapped_lazy, y.m_mapped_lazy)}; } + static value_type inverse(const value_type &x) { return {CommutativeGroup1::inverse(x.m_key_lazy), CommutativeGroup2::inverse(x.m_mapped_lazy)}; } + }; + typename group::value_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(typename group::value_type inc) { m_lazy = group::op(m_lazy, inc); } + key_type _ki2o(const key_type &k) const { return CommutativeGroup1::op(k, m_lazy.m_key_lazy); } + key_type _ko2i(const key_type &k) const { return CommutativeGroup1::op(k, CommutativeGroup1::inverse(m_lazy.m_key_lazy)); } + void _pushdown(inner_mapped_type &p) const { p.m_val = CommutativeGroup2::op(p.m_val, CommutativeGroup2::op(p.m_inv, m_lazy.m_mapped_lazy)), p.m_inv = CommutativeGroup2::inverse(m_lazy.m_mapped_lazy); } + template + It _reduced(It it) const { return _pushdown(it->second), it; } + inner_mapped_type _make_mapped(const mapped_type &m) const { return {m, CommutativeGroup2::inverse(m_lazy.m_mapped_lazy)}; } + static mapped_type &_get(inner_mapped_type &p) { return p.m_val; } + }; + template + struct TableBase { + using key_type = Key; + using mapped_type = Table; + using inner_mapped_type = typename Trait::pair; + using group = typename mapped_type::group; + typename group::value_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(typename group::value_type inc) { m_lazy = group::op(m_lazy, inc); } + const Key &_ki2o(const Key &k) const { return k; } + const Key &_ko2i(const Key &k) const { return k; } + void _pushdown(inner_mapped_type &p) const { p.m_val.globally_add(group::op(p.m_inv, m_lazy)), p.m_inv = group::inverse(m_lazy); } + template + It _reduced(It it) const { return _pushdown(it->second), it; } + inner_mapped_type _make_mapped(mapped_type &&m) const { return {std::move(m), group::inverse(m_lazy)}; } + static mapped_type &_get(inner_mapped_type &p) { return p.m_val; } + }; + template + struct TableBase, Table, true> { + using key_type = typename CommutativeGroup::value_type; + using mapped_type = Table; + using inner_mapped_type = typename Trait::pair; + using sub_group = typename mapped_type::group; + struct group { + struct value_type { + key_type m_key_lazy; + typename sub_group::value_type m_mapped_lazy; + }; + static value_type identity() { return {CommutativeGroup::identity(), sub_group::identity()}; } + static value_type op(const value_type &x, const value_type &y) { return {CommutativeGroup::op(x.m_key_lazy, y.m_key_lazy), sub_group::op(x.m_mapped_lazy, y.m_mapped_lazy)}; } + static value_type inverse(const value_type &x) { return {CommutativeGroup::inverse(x.m_key_lazy), sub_group::inverse(x.m_mapped_lazy)}; } + }; + typename group::value_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(typename group::value_type inc) { m_lazy = group::op(m_lazy, inc); } + key_type _ki2o(const key_type &k) const { return CommutativeGroup::op(k, m_lazy.m_key_lazy); } + key_type _ko2i(const key_type &k) const { return CommutativeGroup::op(k, CommutativeGroup::inverse(m_lazy.m_key_lazy)); } + void _pushdown(inner_mapped_type &p) const { p.m_val.globally_add(sub_group::op(p.m_inv, m_lazy.m_mapped_lazy)), p.m_inv = sub_group::inverse(m_lazy.m_mapped_lazy); } + template + It _reduced(It it) const { return _pushdown(it->second), it; } + inner_mapped_type _make_mapped(mapped_type &&m) const { return {std::move(m), sub_group::inverse(m_lazy.m_mapped_lazy)}; } + static mapped_type &_get(inner_mapped_type &p) { return p.m_val; } + }; + template ::type>, typename Pred = std::equal_to::type>> + struct Table : TableBase { + using base = TableBase; + using key_type = typename base::key_type; + using mapped_type = typename base::mapped_type; + using inner_mapped_type = typename base::inner_mapped_type; + using inner_table = std::unordered_map; + using iterator = typename inner_table::iterator; + mutable inner_table m_map; + Table() { base::clear(); } + iterator begin() const { + auto it = m_map.begin(); + return it == m_map.end() ? it : base::_reduced(it); + } + iterator end() const { return m_map.end(); } + iterator find(key_type k) const { + auto it = m_map.find(base::_ko2i(k)); + return it == m_map.end() ? it : base::_reduced(it); + } + bool contains(key_type k) const { return find(k) != m_map.end(); } + bool empty() const { return m_map.empty(); } + auto size() const -> decltype(m_map.size()) { return m_map.size(); } + void clear() { inner_table().swap(m_map), base::clear(); } + auto insert_or_assign(key_type k, mapped_type m) -> decltype(m_map.emplace(k, inner_mapped_type())) { + auto res = m_map.emplace(base::_ko2i(k), base::_make_mapped(std::move(m))); + if (!res.second) base::_pushdown(res.first->second), base::_get(res.first->second) = m; + return res; + } + auto insert_or_ignore(key_type k, mapped_type m) -> decltype(m_map.emplace(k, inner_mapped_type())) { return m_map.emplace(base::_ko2i(k), base::_make_mapped(std::move(m))); } + template + auto insert_or_modify(key_type k, Callback &&call) -> decltype(m_map.emplace(k, inner_mapped_type())) { + auto res = m_map.emplace(base::_ko2i(k), base::_make_mapped({})); + base::_pushdown(res.first->second); + call(base::_get(res.first->second), res.second); + return res; + } + bool erase(key_type k) { return m_map.erase(base::_ko2i(k)); } + const mapped_type &get(key_type k, const mapped_type &_default) const { + auto res = find(k); + return res != m_map.end() ? base::_get(res->second) : _default; + } + const mapped_type &get(key_type k) const { return base::_get(find(k)->second); } + mapped_type &get(key_type k) { + auto res = m_map.emplace(base::_ko2i(k), base::_make_mapped({})); + return base::_get(base::_reduced(res.first)->second); + } + template + void enumerate(Callback &&call) const { + for (auto it = m_map.begin(); it != m_map.end(); ++it) { + base::_pushdown(it->second); + call(base::_ki2o(it->first), base::_get(it->second)); + } + } + }; + template + Ostream &operator<<(Ostream &out, const Table &x) { + out << '{'; + bool started = false; + auto call = [&](const typename LAZYIMP::Trait::type &k, const typename Table::mapped_type &v) { + if (started) + out << ", "; + else + started = true; + out << k << ": " << v; + }; + x.enumerate(call); + return out << '}'; + } + } + namespace LAZYIMP { + template + struct Trait> { + struct pair { + LAZYUMAP::Table m_val; + typename LAZYUMAP::Table::group::value_type m_inv; + }; + static constexpr bool is_table = true; + }; + } + template ::type>, typename Pred = std::equal_to::type>> + using LazyKeyAddUmap = LAZYUMAP::Table, Mapped, Hash, Pred>; + template ::type>, typename Pred = std::equal_to::type>> + using LazyMappedAddUmap = LAZYUMAP::Table, Hash, Pred>; + template ::type>, typename Pred = std::equal_to::type>> + using LazyKeyAddMappedAddUmap = LAZYUMAP::Table, LAZYUMAP::LazyAdd, Hash, Pred>; +} + +#endif \ No newline at end of file diff --git a/MISC/LazyUmap.md b/MISC/LazyUmap.md new file mode 100644 index 0000000..0110cad --- /dev/null +++ b/MISC/LazyUmap.md @@ -0,0 +1,164 @@ +### 一、模板类别 + +​ 数据结构:全局懒修改的 `unordered_map` 。 + +​ 练习题目: + +1. [k - 路径(hard vension)](https://ac.nowcoder.com/acm/problem/279411) + + +### 二、模板功能 + +​ 本模板对 `std::unordered_map` 进行一个封装,基于差分,对 `key` 和 `mapped` 实现了全局懒修改。 + +​ 本模板对 `Key` 和 `Mapped` ,都可以通过 `typename CommutativeGroup` 封装一个支持懒修改的交换群。交换群须满足以下要求: + +1. 声明 `value_type` 为值类型; + +2. 定义静态函数 `op` ,接受两个 `value_type` 参数,返回它们的聚合值; + +3. 定义静态函数 `identity` ,无输入参数,返回幺元。 + +4. 定义静态函数 `inverse` ,输入参数一个 `value_type` ,返回其逆元。 + +​ 本模板要求运算符满足**结合律**和**交换律**。常见的交换群为加法群和异或群。 + +**注意:** + +​ 不推荐通过迭代器访问 `first` 获取键,因为还需要算上全局懒标记的影响,才是真正的键; + +​ 通过 `find` 以及插入所获取的迭代器,访问 `second` 获取值是合理的,因为懒标记的影响已更新; + +​ 通过迭代器前移后移获取到的迭代器,访问 `second` 获取值是不合理的,因为懒标记的影响未更新。 + +### 三、模板示例 + +```c++ +#include "IO/FastIO.h" +#include "MISC/LazyUmap.h" + +void test_key_add() { + // key_type 可以全局加 + OY::LazyKeyAddUmap mp; + mp.insert_or_assign(100, "A"); + mp.insert_or_assign(300, "C"); + mp.globally_add(10); + mp.insert_or_assign(200, "B"); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_mapped_add() { + // mapped_type 可以全局加 + OY::LazyMappedAddUmap mp; + mp.insert_or_assign("A", 100); + mp.insert_or_assign("C", 300); + mp.globally_add(10); + mp.insert_or_assign("B", 200); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_key_add_mapped_add() { + // key_type 和 mapped_type 都可以全局加 + OY::LazyKeyAddMappedAddUmap mp1; + mp1.insert_or_assign(100, 1000); + mp1.globally_add({20, 0}); + mp1.insert_or_assign(200, 2000); + mp1.globally_add({0, 3}); + + OY::LazyKeyAddMappedAddUmap mp2; + mp2.insert_or_assign(400, 4000); + mp2.globally_add({0, 5}); + mp2.insert_or_assign(300, 3000); + mp2.globally_add({40, 0}); + mp2.insert_or_assign(200, 70000); + + // map 合并 + mp2.enumerate([&](int k, int v) { + mp1.insert_or_modify(k, [&](int &old, bool flag) { + if (flag) + old = v; + else + old += v; + }); + }); + mp2.clear(); + + // 看看结果 + cout << mp1 << "\n\n"; +} + +void test_key_xor_mapped_mul() { + // 设计一个 key 可以全局异或, mapped 可以全局乘 的 map + + // key 支持异或很简单,有现成的 + using Key = OY::LAZYUMAP::LazyBitXor; + + // mapped 支持乘法需要写一个交换群 + struct monoid { + using value_type = double; + static double identity() { return 1; } + static double op(double x, double y) { return x * y; } + static double inverse(double x) { return 1 / x; } + }; + using Mapped = OY::LAZYUMAP::LazyType; + + // 组合成一个 map + using T = OY::LAZYUMAP::Table; + + // 试试效果 + T mp; + mp.insert_or_assign(18, 10.1); + mp.insert_or_assign(32, 30.3); + mp.globally_add({4, 1}); + mp.insert_or_assign(25, 0.5); + mp.globally_add({0, 0.1}); + mp.globally_add({12, 1}); + + cout << mp << "\n\n"; +} + +void test_map_map() { + // 二维 map + // key_type 和 mapped_type 都可以全局加 + using Map1 = OY::LazyKeyAddMappedAddUmap; + using Map2 = OY::LAZYUMAP::Table, Map1>; + Map2 mp; + mp.insert_or_assign(10, {}); + mp.insert_or_assign(20, {}); + mp.globally_add({1}); + mp.get(20).insert_or_assign(200, 2000); + mp.insert_or_assign(30, {}); + mp.globally_add({2, {10, 0}}); + mp.get(10).insert_or_assign(100, 1000); + + cout << mp << "\n\n"; +} + +int main() { + test_key_add(); + test_mapped_add(); + test_key_add_mapped_add(); + test_key_xor_mapped_mul(); + test_map_map(); +} +``` + +``` +#输出如下 +{202: B, 312: C, 112: A} + +{B: 202, C: 312, A: 112} + +{440: 4005, 340: 3000, 200: 72003, 120: 1003} + +{21: 0.050000, 40: 3.030000, 26: 1.010000} + +{32: {}, 22: {210: 2000}, 10: {100: 1000}, 23: {}, 13: {}} + + +``` + diff --git a/MISC/LazyUset.h b/MISC/LazyUset.h new file mode 100644 index 0000000..7604625 --- /dev/null +++ b/MISC/LazyUset.h @@ -0,0 +1,121 @@ +/* +最后修改: +20241026 +测试环境: +gcc11.2,c++11 +clang12.0,C++11 +msvc14.2,C++14 +*/ +#ifndef __OY_LAZYUSET__ +#define __OY_LAZYUSET__ + +#include +#include +#include +#include + +namespace OY { +#ifndef __OY_LAZYIMP__ +#define __OY_LAZYIMP__ + namespace LAZYIMP { + template + struct AddCommutativeGroup { + using value_type = Tp; + static Tp identity() { return Tp{}; } + static Tp op(const Tp &x, const Tp &y) { return x + y; } + static Tp inverse(const Tp &x) { return -x; } + }; + template + struct BitxorCommutativeGroup { + using value_type = Tp; + static Tp identity() { return Tp{}; } + static Tp op(const Tp &x, const Tp &y) { return x ^ y; } + static Tp inverse(const Tp &x) { return x; } + }; + template + struct LazyType {}; + template + using LazyAdd = LazyType>; + template + using LazyBitXor = LazyType>; + template + struct Trait { + using type = Tp; + static constexpr bool is_table = false; + }; + template + struct Trait> { + using type = typename CommutativeGroup::value_type; + struct pair { + type m_val, m_inv; + }; + static constexpr bool is_table = false; + }; + } +#endif + namespace LAZYUSET { + using namespace LAZYIMP; + template + struct TableBase; + template + struct TableBase> { + using key_type = typename CommutativeGroup::value_type; + using group = CommutativeGroup; + key_type m_lazy; + void clear() { m_lazy = group::identity(); } + void globally_add(key_type inc) { m_lazy = group::op(m_lazy, inc); } + key_type _ki2o(const key_type &k) const { return group::op(k, m_lazy); } + key_type _ko2i(const key_type &k) const { return group::op(k, group::inverse(m_lazy)); } + }; + template ::type>, typename Pred = std::equal_to::type>> + struct Table : TableBase { + using base = TableBase; + using key_type = typename base::key_type; + using inner_table = std::unordered_set; + using iterator = typename inner_table::iterator; + mutable inner_table m_set; + Table() { base::clear(); } + iterator begin() const { return m_set.begin(); } + iterator end() const { return m_set.end(); } + iterator find(key_type k) const { return m_set.find(base::_ko2i(k)); } + bool contains(key_type k) const { return find(k) != m_set.end(); } + bool empty() const { return m_set.empty(); } + auto size() const -> decltype(m_set.size()) { return m_set.size(); } + void clear() { inner_table().swap(m_set), base::clear(); } + auto insert(key_type k) -> decltype(m_set.emplace(k)) { return m_set.insert(base::_ko2i(k)); } + bool erase(key_type k) { return m_set.erase(base::_ko2i(k)); } + template + void enumerate(Callback &&call) const { + for (auto it = m_set.begin(); it != m_set.end(); ++it) call(base::_ki2o(*it)); + } + }; + template + Ostream &operator<<(Ostream &out, const Table &x) { + out << '{'; + bool started = false; + auto call = [&](const typename LAZYIMP::Trait::type &k) { + if (started) + out << ", "; + else + started = true; + out << k; + }; + x.enumerate(call); + return out << '}'; + } + } + namespace LAZYIMP { + template + struct Trait> { + struct pair { + LAZYUSET::Table m_val; + typename LAZYUSET::Table::group::value_type m_inv; + }; + static constexpr bool is_table = true; + }; + } + template ::type>, typename Pred = std::equal_to::type>> + using LazyKeyAddUset = LAZYUSET::Table, Hash, Pred>; +} + +#endif \ No newline at end of file diff --git a/MISC/LazyUset.md b/MISC/LazyUset.md new file mode 100644 index 0000000..8f21067 --- /dev/null +++ b/MISC/LazyUset.md @@ -0,0 +1,84 @@ +### 一、模板类别 + +​ 数据结构:全局懒修改的 `unordered_set` 。 + +​ 练习题目: + +1. [P3806 【模板】点分治1](https://www.luogu.com.cn/problem/P3806) + + +### 二、模板功能 + +​ 本模板对 `std::unordered_set` 进行一个封装,基于差分,对 `key` 实现了全局懒修改。 + +​ 本模板对 `Key` ,可以通过 `typename CommutativeGroup` 封装一个支持懒修改的交换群。交换群须满足以下要求: + +1. 声明 `value_type` 为值类型; + +2. 定义静态函数 `op` ,接受两个 `value_type` 参数,返回它们的聚合值; + +3. 定义静态函数 `identity` ,无输入参数,返回幺元。 + +4. 定义静态函数 `inverse` ,输入参数一个 `value_type` ,返回其逆元。 + +​ 本模板要求运算符满足**结合律**和**交换律**。常见的交换群为加法群和异或群。 + +**注意:** + +​ 不推荐通过迭代器解引用获取键,因为还需要算上全局懒标记的影响,才是真正的键; + +### 三、模板示例 + +```c++ +#include "IO/FastIO.h" +#include "MISC/LazyUset.h" + +void test_key_add() { + // key_type 可以全局加 + OY::LazyKeyAddUset mp; + mp.insert(100); + mp.insert(300); + mp.globally_add(10); + mp.insert(200); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_key_mul() { + // 设计一个 key 可以全局乘 的 set + + // 需要写一个交换群 + struct monoid { + using value_type = double; + static double identity() { return 1; } + static double op(double x, double y) { return x * y; } + static double inverse(double x) { return 1 / x; } + }; + using T = OY::LAZYUSET::Table>; + + // 试试效果 + T mp; + mp.insert(10.1); + mp.insert(30.3); + mp.globally_add(4); + mp.insert(25); + mp.globally_add(0.1); + + cout << mp << "\n\n"; +} + +int main() { + test_key_add(); + test_key_mul(); +} +``` + +``` +#输出如下 +{202, 312, 112} + +{2.500000, 12.120000, 4.040000} + +``` + diff --git a/TEST/local/ErasableMonoPairHeap_test.cpp b/TEST/local/ErasableMonoPairHeap_test.cpp new file mode 100644 index 0000000..42861cd --- /dev/null +++ b/TEST/local/ErasableMonoPairHeap_test.cpp @@ -0,0 +1,26 @@ +#include "DS/ErasableMonoPairHeap.h" +#include "IO/FastIO.h" + +void test() { + OY::VectorErasableMonoSumPairHeap S; + S.push(100); + S.push(400); + S.push(200); + S.push(300); + S.push(500); + S.erase(200); + S.erase(400); + cout << "size = " << S.size() << endl; + S.pop(); + cout << "sum = " << S.query_all() << endl; +} + +int main() { + test(); +} +/* +#输出如下 +size = 3 +sum = 400 + +*/ \ No newline at end of file diff --git a/TEST/local/LazyMap_test.cpp b/TEST/local/LazyMap_test.cpp new file mode 100644 index 0000000..016b50d --- /dev/null +++ b/TEST/local/LazyMap_test.cpp @@ -0,0 +1,124 @@ +#include "IO/FastIO.h" +#include "MISC/LazyMap.h" + +void test_key_add() { + // key_type 可以全局加 + OY::LazyKeyAddMap mp; + mp.insert_or_assign(100, "A"); + mp.insert_or_assign(300, "C"); + mp.globally_add(10); + mp.insert_or_assign(200, "B"); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_mapped_add() { + // mapped_type 可以全局加 + OY::LazyMappedAddMap mp; + mp.insert_or_assign("A", 100); + mp.insert_or_assign("C", 300); + mp.globally_add(10); + mp.insert_or_assign("B", 200); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_key_add_mapped_add() { + // key_type 和 mapped_type 都可以全局加 + OY::LazyKeyAddMappedAddMap mp1; + mp1.insert_or_assign(100, 1000); + mp1.globally_add({20, 0}); + mp1.insert_or_assign(200, 2000); + mp1.globally_add({0, 3}); + + OY::LazyKeyAddMappedAddMap mp2; + mp2.insert_or_assign(400, 4000); + mp2.globally_add({0, 5}); + mp2.insert_or_assign(300, 3000); + mp2.globally_add({40, 0}); + mp2.insert_or_assign(200, 70000); + + // map 合并 + mp2.enumerate([&](int k, int v) { + mp1.insert_or_modify(k, [&](int &old, bool flag) { + if (flag) + old = v; + else + old += v; + }); + }); + mp2.clear(); + + // 看看结果 + cout << mp1 << "\n\n"; +} + +void test_key_xor_mapped_mul() { + // 设计一个 key 可以全局异或, mapped 可以全局乘 的 map + + // key 支持异或很简单,有现成的 + using Key = OY::LAZYMAP::LazyBitXor; + + // mapped 支持乘法需要写一个交换群 + struct monoid { + using value_type = double; + static double identity() { return 1; } + static double op(double x, double y) { return x * y; } + static double inverse(double x) { return 1 / x; } + }; + using Mapped = OY::LAZYMAP::LazyType; + + // 组合成一个 map + using T = OY::LAZYMAP::Table; + + // 试试效果 + T mp; + mp.insert_or_assign(18, 10.1); + mp.insert_or_assign(32, 30.3); + mp.globally_add({4, 1}); + mp.insert_or_assign(25, 0.5); + mp.globally_add({0, 0.1}); + mp.globally_add({12, 1}); + + cout << mp << "\n\n"; +} + +void test_map_map() { + // 二维 map + // key_type 和 mapped_type 都可以全局加 + using Map1 = OY::LazyKeyAddMappedAddMap; + using Map2 = OY::LAZYMAP::Table, Map1>; + Map2 mp; + mp.insert_or_assign(10, {}); + mp.insert_or_assign(20, {}); + mp.globally_add({1}); + mp.get(20).insert_or_assign(200, 2000); + mp.insert_or_assign(30, {}); + mp.globally_add({2, {10, 0}}); + mp.get(10).insert_or_assign(100, 1000); + + cout << mp << "\n\n"; +} + +int main() { + test_key_add(); + test_mapped_add(); + test_key_add_mapped_add(); + test_key_xor_mapped_mul(); + test_map_map(); +} +/* +#输出如下 +{112: A, 202: B, 312: C} + +{A: 112, B: 202, C: 312} + +{120: 1003, 200: 72003, 340: 3000, 440: 4005} + +{26: 1.010000, 21: 0.050000, 40: 3.030000} + +{10: {100: 1000}, 13: {}, 22: {210: 2000}, 23: {}, 32: {}} + +*/ \ No newline at end of file diff --git a/TEST/local/LazySet_test.cpp b/TEST/local/LazySet_test.cpp new file mode 100644 index 0000000..e209a41 --- /dev/null +++ b/TEST/local/LazySet_test.cpp @@ -0,0 +1,49 @@ +#include "IO/FastIO.h" +#include "MISC/LazySet.h" + +void test_key_add() { + // key_type 可以全局加 + OY::LazyKeyAddSet mp; + mp.insert(100); + mp.insert(300); + mp.globally_add(10); + mp.insert(200); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_key_mul() { + // 设计一个 key 可以全局乘 的 set + + // 需要写一个交换群 + struct monoid { + using value_type = double; + static double identity() { return 1; } + static double op(double x, double y) { return x * y; } + static double inverse(double x) { return 1 / x; } + }; + using T = OY::LAZYSET::Table>; + + // 试试效果 + T mp; + mp.insert(10.1); + mp.insert(30.3); + mp.globally_add(4); + mp.insert(25); + mp.globally_add(0.1); + + cout << mp << "\n\n"; +} + +int main() { + test_key_add(); + test_key_mul(); +} +/* +#输出如下 +{112, 202, 312} + +{2.500000, 4.040000, 12.120000} + +*/ \ No newline at end of file diff --git a/TEST/local/LazyUmap_test.cpp b/TEST/local/LazyUmap_test.cpp new file mode 100644 index 0000000..da5908c --- /dev/null +++ b/TEST/local/LazyUmap_test.cpp @@ -0,0 +1,124 @@ +#include "IO/FastIO.h" +#include "MISC/LazyUmap.h" + +void test_key_add() { + // key_type 可以全局加 + OY::LazyKeyAddUmap mp; + mp.insert_or_assign(100, "A"); + mp.insert_or_assign(300, "C"); + mp.globally_add(10); + mp.insert_or_assign(200, "B"); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_mapped_add() { + // mapped_type 可以全局加 + OY::LazyMappedAddUmap mp; + mp.insert_or_assign("A", 100); + mp.insert_or_assign("C", 300); + mp.globally_add(10); + mp.insert_or_assign("B", 200); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_key_add_mapped_add() { + // key_type 和 mapped_type 都可以全局加 + OY::LazyKeyAddMappedAddUmap mp1; + mp1.insert_or_assign(100, 1000); + mp1.globally_add({20, 0}); + mp1.insert_or_assign(200, 2000); + mp1.globally_add({0, 3}); + + OY::LazyKeyAddMappedAddUmap mp2; + mp2.insert_or_assign(400, 4000); + mp2.globally_add({0, 5}); + mp2.insert_or_assign(300, 3000); + mp2.globally_add({40, 0}); + mp2.insert_or_assign(200, 70000); + + // map 合并 + mp2.enumerate([&](int k, int v) { + mp1.insert_or_modify(k, [&](int &old, bool flag) { + if (flag) + old = v; + else + old += v; + }); + }); + mp2.clear(); + + // 看看结果 + cout << mp1 << "\n\n"; +} + +void test_key_xor_mapped_mul() { + // 设计一个 key 可以全局异或, mapped 可以全局乘 的 map + + // key 支持异或很简单,有现成的 + using Key = OY::LAZYUMAP::LazyBitXor; + + // mapped 支持乘法需要写一个交换群 + struct monoid { + using value_type = double; + static double identity() { return 1; } + static double op(double x, double y) { return x * y; } + static double inverse(double x) { return 1 / x; } + }; + using Mapped = OY::LAZYUMAP::LazyType; + + // 组合成一个 map + using T = OY::LAZYUMAP::Table; + + // 试试效果 + T mp; + mp.insert_or_assign(18, 10.1); + mp.insert_or_assign(32, 30.3); + mp.globally_add({4, 1}); + mp.insert_or_assign(25, 0.5); + mp.globally_add({0, 0.1}); + mp.globally_add({12, 1}); + + cout << mp << "\n\n"; +} + +void test_map_map() { + // 二维 map + // key_type 和 mapped_type 都可以全局加 + using Map1 = OY::LazyKeyAddMappedAddUmap; + using Map2 = OY::LAZYUMAP::Table, Map1>; + Map2 mp; + mp.insert_or_assign(10, {}); + mp.insert_or_assign(20, {}); + mp.globally_add({1}); + mp.get(20).insert_or_assign(200, 2000); + mp.insert_or_assign(30, {}); + mp.globally_add({2, {10, 0}}); + mp.get(10).insert_or_assign(100, 1000); + + cout << mp << "\n\n"; +} + +int main() { + test_key_add(); + test_mapped_add(); + test_key_add_mapped_add(); + test_key_xor_mapped_mul(); + test_map_map(); +} +/* +#输出如下 +{202: B, 312: C, 112: A} + +{B: 202, C: 312, A: 112} + +{440: 4005, 340: 3000, 200: 72003, 120: 1003} + +{21: 0.050000, 40: 3.030000, 26: 1.010000} + +{32: {}, 22: {210: 2000}, 10: {100: 1000}, 23: {}, 13: {}} + +*/ \ No newline at end of file diff --git a/TEST/local/LazyUset_test.cpp b/TEST/local/LazyUset_test.cpp new file mode 100644 index 0000000..8cfa6c3 --- /dev/null +++ b/TEST/local/LazyUset_test.cpp @@ -0,0 +1,49 @@ +#include "IO/FastIO.h" +#include "MISC/LazyUset.h" + +void test_key_add() { + // key_type 可以全局加 + OY::LazyKeyAddUset mp; + mp.insert(100); + mp.insert(300); + mp.globally_add(10); + mp.insert(200); + mp.globally_add(2); + + cout << mp << "\n\n"; +} + +void test_key_mul() { + // 设计一个 key 可以全局乘 的 set + + // 需要写一个交换群 + struct monoid { + using value_type = double; + static double identity() { return 1; } + static double op(double x, double y) { return x * y; } + static double inverse(double x) { return 1 / x; } + }; + using T = OY::LAZYUSET::Table>; + + // 试试效果 + T mp; + mp.insert(10.1); + mp.insert(30.3); + mp.globally_add(4); + mp.insert(25); + mp.globally_add(0.1); + + cout << mp << "\n\n"; +} + +int main() { + test_key_add(); + test_key_mul(); +} +/* +#输出如下 +{202, 312, 112} + +{2.500000, 12.120000, 4.040000} + +*/ \ No newline at end of file diff --git a/TEST/oj/hdoj_2888.cpp b/TEST/oj/hdoj_2888.cpp index 2c841b6..a40592d 100644 --- a/TEST/oj/hdoj_2888.cpp +++ b/TEST/oj/hdoj_2888.cpp @@ -45,7 +45,12 @@ void solve_mdseg() { for (uint32_t i = 0; i < m; i++) for (uint32_t j = 0; j < n; j++) cin >> A[i][j]; using base_table = OY::MaskRMQMaxValueTable; - OY::MonoMaxMDSeg S(m * n); + struct monoid { + using value_type = uint32_t; + static value_type op(value_type x, value_type y) { return std::max(x, y); } + static value_type identity() { return 0; } + }; + OY::MonoMaxMDSeg S(m * n); for (uint32_t i = 0; i < m; i++) for (uint32_t j = 0; j < n; j++) S.add_point(A[i][j], i, j); S.prepare(); diff --git a/TEST/oj/lc_3321.cpp b/TEST/oj/lc_3321.cpp index b259013..996e306 100644 --- a/TEST/oj/lc_3321.cpp +++ b/TEST/oj/lc_3321.cpp @@ -1,3 +1,4 @@ +#include "DS/ErasableMonoPairHeap.h" #include "DS/MonoAVL.h" #include "DS/MonoSplay.h" #include "IO/LeetcodeIO.h" @@ -9,21 +10,86 @@ using namespace std; */ /** * 滑动窗口模板,平衡树模板 + * 当然效率最高的是对顶堆 */ class Solution { -public: - vector findXSum(vector &nums, int k, int x) { + vector solve_heap(vector &nums, int k, int x) { + // 本平衡树维护两个属性的和 + // 属性一是频率 + // 属性二是键和频率的乘积 + struct Monoid { + using value_type = pair; + struct sum_type { + long long v; + sum_type() = default; + sum_type(long long v) : v(v) {} + sum_type(value_type v) : v(v.second) {} + operator value_type() const { return {}; } + }; + static sum_type op(const sum_type &x, const sum_type &y) { return {x.v + y.v}; } + static sum_type identity() { return {}; } + static sum_type inverse(const sum_type &x) { return {-x.v}; } + }; + static bool init = false; + if (!init) init = true, OY::EMONOPH::Heap>::_reserve(200000), OY::EMONOPH::Heap>::_reserve(400000); + OY::EMONOPH::Heap> smaller; + OY::EMONOPH::Heap> bigger; + auto erase = [&](pair &&e) { + if (!smaller.empty() && e <= smaller.top()) + smaller.erase(e); + else { + bigger.erase(e); + if (!smaller.empty()) bigger.push(smaller.top()), smaller.pop(); + } + }; + auto insert = [&](pair &&e) { + bigger.push(e); + if (bigger.size() > x) smaller.push(bigger.top()), bigger.pop(); + }; + // mp 维护频率 + unordered_map mp; + vector ans; + // 给固定大小的滑动窗口传递回调函数 + // 对已就绪的窗口调用的回调 + auto call = [&](int l, int r) { + ans.push_back(bigger.query_all().v); + }; + // 左端点右移的回调 + auto left_call = [&](int i) { + auto &cnt = mp[nums[i]]; + if (cnt) erase({cnt, 1ll * nums[i] * cnt}); + --cnt; + if (cnt) insert({cnt, 1ll * nums[i] * cnt}); + }; + // 右端点右移的回调 + auto right_call = [&](int i) { + auto &cnt = mp[nums[i]]; + if (cnt) erase({cnt, 1ll * nums[i] * cnt}); + ++cnt; + if (cnt) insert({cnt, 1ll * nums[i] * cnt}); + }; + OY::WINDOW::solve(nums.size(), k, call, left_call, right_call); + return ans; + } + vector solve_avl(vector &nums, int k, int x) { // 本平衡树维护两个属性的和 // 属性一是频率 // 属性二是键和频率的乘积 struct Monoid { using value_type = pair; - static value_type op(value_type x, value_type y) { return {x.first + y.first, x.second + y.second}; } - static value_type identity() { return {0, 0}; } + struct sum_type { + long long v; + sum_type() = default; + sum_type(long long v) : v(v) {} + sum_type(value_type v) : v(v.second) {} + operator value_type() const { return {}; } + }; + static sum_type op(const sum_type &x, const sum_type &y) { return {x.v + y.v}; } + static sum_type identity() { return {}; } }; - using Tree = OY::MONOAVL::Tree; - // using Tree = OY::MONOSPLAY::Tree; + // using Tree = OY::MONOAVL::Tree; + using Tree = OY::MONOSPLAY::Tree; Tree S; // mp 维护频率 unordered_map mp; @@ -32,9 +98,9 @@ class Solution { // 对已就绪的窗口调用的回调 auto call = [&](int l, int r) { if (S.size() < x) - ans.push_back(S.query_all().second); + ans.push_back(S.query_all().v); else - ans.push_back(S.query(S.size() - x, S.size() - 1).second); + ans.push_back(S.query(S.size() - x, S.size() - 1).v); }; // 左端点右移的回调 auto left_call = [&](int i) { @@ -53,6 +119,12 @@ class Solution { OY::WINDOW::solve(nums.size(), k, call, left_call, right_call); return ans; } + +public: + vector findXSum(vector &nums, int k, int x) { + return solve_heap(nums, k, x); + // return solve_avl(nums, k, x); + } }; #ifdef OY_LOCAL diff --git a/TEST/oj/luogu_p1801.cpp b/TEST/oj/luogu_p1801.cpp new file mode 100644 index 0000000..8759d95 --- /dev/null +++ b/TEST/oj/luogu_p1801.cpp @@ -0,0 +1,134 @@ +#include "DS/CompressedTree.h" +#include "DS/GlobalHashBIT.h" +#include "DS/MergeSortTree.h" +#include "DS/MonoAVL.h" +#include "DS/MonoPairHeap.h" +#include "DS/MonoSplay.h" +#include "DS/SegCounter.h" +#include "DS/StaticBufferWrapWithCollect.h" +#include "DS/WaveLet.h" +#include "IO/FastIO.h" + +/* +[P1801 黑匣子](https://www.luogu.com.cn/problem/P1801) +*/ +/** + * 本题为模板题 + * 由于查找的 kth 是连续变化的,所以可以用对顶堆维护 + * 也可以简单粗暴用平衡树或者线段树找 kth + */ + +int arr[200000]; +void solve_heap() { + uint32_t n, q; + cin >> n >> q; + OY::MonoPairHeap, OY::StaticBufferWrapWithCollect<200000>::type> smaller; + OY::MonoPairHeap, OY::StaticBufferWrapWithCollect<200000>::type> bigger; + auto insert = [&](auto &&e) { + smaller.push(e), bigger.push(smaller.top()), smaller.pop(); + }; + for (uint32_t i = 0; i != n; i++) cin >> arr[i]; + uint32_t cur = 0; + for (uint32_t i = 0; i != q; i++) { + uint32_t x; + cin >> x; + while (cur != x) insert(arr[cur++]); + smaller.push(bigger.top()), bigger.pop(); + cout << smaller.top() << endl; + } +} + +void solve_avl() { + uint32_t n, q; + cin >> n >> q; + OY::MonoAVLSequence::type> tr; + // OY::MonoSplaySequence::type> tr; + auto insert = [&](auto &&e) { + tr.insert_by_comparator(e); + }; + for (uint32_t i = 0; i != n; i++) cin >> arr[i]; + uint32_t cur = 0; + for (uint32_t i = 0; i != q; i++) { + uint32_t x; + cin >> x; + while (cur != x) insert(arr[cur++]); + cout << tr.query(i) << endl; + } +} + +void solve_segcnt() { + uint32_t n, q; + cin >> n >> q; + OY::SEGCNT::Table::type> tr; + auto insert = [&](int64_t e) { + tr.add(e + 2000000000, 1); + }; + for (uint32_t i = 0; i != n; i++) cin >> arr[i]; + uint32_t cur = 0; + for (uint32_t i = 0; i != q; i++) { + uint32_t x; + cin >> x; + while (cur != x) insert(arr[cur++]); + cout << int(uint32_t(tr.kth(i)->key()) - 2000000000) << endl; + } +} + +void solve_cptree() { + uint32_t n, q; + cin >> n >> q; + OY::CPTREE::Tree>, uint64_t, OY::StaticBufferWrapWithCollect<400000>::type> tr; + auto insert = [&](int64_t e) { + tr.add(e + 2000000000, 1); + }; + for (uint32_t i = 0; i != n; i++) cin >> arr[i]; + uint32_t cur = 0; + for (uint32_t i = 0; i != q; i++) { + uint32_t x; + cin >> x; + while (cur != x) insert(arr[cur++]); + cout << int(uint32_t(tr.max_right(0, [&](auto cnt) { return cnt <= i; }) + 1) - 2000000000) << endl; + } +} + +OY::GBIT::Tree Gbit(4000000001); +void solve_gbit() { + uint32_t n, q; + cin >> n >> q; + auto insert = [&](int64_t e) { + Gbit.add(e + 2000000000, 1); + }; + for (uint32_t i = 0; i != n; i++) cin >> arr[i]; + uint32_t cur = 0; + for (uint32_t i = 0; i != q; i++) { + uint32_t x; + cin >> x; + while (cur != x) insert(arr[cur++]); + cout << int(uint32_t(Gbit.kth(i)) - 2000000000) << endl; + } +} + +void solve_wavelet() { + uint32_t n, q; + cin >> n >> q; + auto read = [](auto...) { + int x; + cin >> x; + return x; + }; + OY::MS::Tree S(n, read); + // OY::WaveLet::Tree S(n, read); + for (uint32_t i = 0; i != q; i++) { + uint32_t x; + cin >> x; + cout << S.quantile(0, x - 1, i) << endl; + } +} + +int main() { + solve_heap(); + // solve_avl(); + // solve_segcnt(); + // solve_cptree(); + // solve_gbit(); + // solve_wavelet(); +} \ No newline at end of file diff --git a/TEST/oj/luogu_p3806.cpp b/TEST/oj/luogu_p3806.cpp index ff43740..976070b 100644 --- a/TEST/oj/luogu_p3806.cpp +++ b/TEST/oj/luogu_p3806.cpp @@ -1,6 +1,8 @@ #include "IO/FastIO.h" +#include "MISC/LazySet.h" +#include "MISC/LazyUset.h" #include "TREE/Centroid.h" -#include "TREE/LinkTree.h" +#include "TREE/FlatTree.h" #include #include @@ -13,16 +15,15 @@ */ static constexpr uint32_t N = 10000, M = 100; -uint32_t Dis[N]; -bool blocked[N]; bool Ans[M]; std::map> Queries; -int main() { + +uint32_t Dis[N]; +bool blocked[N]; +void solve_centroid() { uint32_t n, m; cin >> n >> m; - OY::LinkTree::Tree S(n); - // OY::FlatTree::Tree S(n); - // OY::VectorTree::Tree S(n); + OY::FlatTree::Tree S(n); for (uint32_t i = 1; i < n; i++) { uint32_t a, b, dis; cin >> a >> b >> dis; @@ -48,14 +49,14 @@ int main() { uint32_t max_Q = Queries.rbegin()->first; Dis[to] = dis; // 此处用返回类型为 bool 的回调函数控制剪枝,返回 true 表示继续,返回 false 就止步 - S.tree_dp_edge(to, [&](uint32_t a, uint32_t p, uint32_t dis) { + auto search_pre = [&](uint32_t a, uint32_t p, uint32_t dis) { if (blocked[a]) return false; if (~p) Dis[a] = Dis[p] + dis; if (Dis[a] > max_Q) return false; curset.insert(Dis[a]); return true; - }, - {}, {}); + }; + S.tree_dp_edge(to, search_pre, {}, {}); if (total.size() < curset.size()) std::swap(total, curset); for (uint32_t a : curset) for (auto it = Queries.lower_bound(a); it != Queries.end();) { @@ -80,3 +81,57 @@ int main() { else cout << "NAY\n"; } + +void solve_merge() { + uint32_t n, m; + cin >> n >> m; + OY::FlatTree::Tree S(n); + for (uint32_t i = 1; i < n; i++) { + uint32_t a, b, dis; + cin >> a >> b >> dis; + S.add_edge(a - 1, b - 1, dis); + } + S.prepare(), S.set_root(0); + + for (uint32_t i = 0; i < m; i++) { + uint32_t Q; + cin >> Q; + Queries[Q].push_back(i); + } + + using LazySet = OY::LazyKeyAddSet; + // using LazySet = OY::LazyKeyAddUset; + std::vector sets(n); + auto pre_work = [&](uint32_t a, uint32_t p, uint32_t e) { sets[a].insert(0); }; + auto report = [&](uint32_t a, uint32_t to, uint32_t e) { + sets[to].globally_add(e); + if (sets[a].size() < sets[to].size()) std::swap(sets[a], sets[to]); + // 寻找匹配 + sets[to].enumerate([&](auto k) { + for (auto it = Queries.begin(); it != Queries.end();) { + auto find = sets[a].find(it->first - k); + if (find != sets[a].end()) { + for (auto idx : it->second) Ans[idx] = true; + it = Queries.erase(it); + } else + ++it; + } + }); + // 合并 + sets[to].enumerate([&](auto k) { + sets[a].insert(k); + }); + }; + S.tree_dp_edge(0, pre_work, report, {}); + + for (uint32_t i = 0; i < m; i++) + if (Ans[i]) + cout << "AYE\n"; + else + cout << "NAY\n"; +} + +int main() { + solve_centroid(); + // solve_merge(); +} \ No newline at end of file diff --git a/TEST/oj/luogu_p4178.cpp b/TEST/oj/luogu_p4178.cpp index f36cc3c..1d02f47 100644 --- a/TEST/oj/luogu_p4178.cpp +++ b/TEST/oj/luogu_p4178.cpp @@ -8,7 +8,7 @@ */ /** * 本题为点分树模板题 - * 由于本题在建好虚树之后就是一个 dp,所以其实可以不用真正地建树,在找到边的时候直接转移即可 + * 由于本题在建好分治树之后就是一个 dp,所以其实可以不用真正地建树,在找到边的时候直接转移即可 */ static constexpr uint32_t N = 40000; uint32_t Dis[N]; diff --git a/TEST/oj/nc_15553.cpp b/TEST/oj/nc_15553.cpp index 9180ce1..9a9e091 100644 --- a/TEST/oj/nc_15553.cpp +++ b/TEST/oj/nc_15553.cpp @@ -5,6 +5,8 @@ [数学考试](https://ac.nowcoder.com/acm/problem/15553) */ /** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/96/H * 本题需要前缀和差分计算子区间和 */ diff --git a/TEST/oj/nc_204829.cpp b/TEST/oj/nc_204829.cpp index 06ddc4f..6693048 100644 --- a/TEST/oj/nc_204829.cpp +++ b/TEST/oj/nc_204829.cpp @@ -7,6 +7,8 @@ [牛牛的等差数列](https://ac.nowcoder.com/acm/problem/204829) */ /** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/5157/C * 考虑一个数组 a 的一阶、二阶、三阶前缀和分别为 S1, S2, S3 * a 的单点修改,等价于 S1 的区间常量修改,等价于 S2 的区间等差数列修改 * S2 的区间和,等价于 S3 的单点查询的差分 diff --git a/TEST/oj/nc_226172.cpp b/TEST/oj/nc_226172.cpp index c00fb3a..41fbbf8 100644 --- a/TEST/oj/nc_226172.cpp +++ b/TEST/oj/nc_226172.cpp @@ -7,6 +7,8 @@ [智乃酱的平方数列](https://ac.nowcoder.com/acm/problem/226172) */ /** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/19684/G * 考虑一个数组 a 的一阶、二阶、三阶前缀和分别为 S1, S2, S3, S4 * a 的单点修改,等价于 S1 的区间常量修改,等价于 S2 的区间等差数列修改,等价于 S3 的区间二次函数修改 * S3 的区间和,等价于 S4 的单点查询的差分 diff --git a/TEST/oj/nc_244121.cpp b/TEST/oj/nc_244121.cpp index af2325d..d43c28c 100644 --- a/TEST/oj/nc_244121.cpp +++ b/TEST/oj/nc_244121.cpp @@ -6,9 +6,12 @@ [剖分](https://ac.nowcoder.com/acm/problem/244121) */ /** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/47266/E * 本题要先进行树上路径增值,子树增值,最后进行单点查询 * 为树上差分模板题 */ + static constexpr uint32_t N = 10000000; int main() { uint32_t n, m; diff --git a/TEST/oj/nc_244326.cpp b/TEST/oj/nc_244326.cpp index 0bd93d5..dab360d 100644 --- a/TEST/oj/nc_244326.cpp +++ b/TEST/oj/nc_244326.cpp @@ -6,6 +6,8 @@ [炫酷反演魔术](https://ac.nowcoder.com/acm/problem/244326) */ /** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/43058/E * 求 \Sigma_{i=1}^n \Sigma_{j=1}^n \phi(gcd(a[i],a[j]^3)) * 恰好 a[i] 值域也在 1~n,设 cnt(i) 为 a 数组里值 i 出现次数 * 即 \Sigma_{i=1}^n \Sigma_{j=1}^n \phi(gcd(i,j^3)) cnt(i) cnt(j) @@ -29,6 +31,43 @@ */ static constexpr uint32_t N = 300000; +void solve_inex() { + // 考虑莫比乌斯反演本质是容斥 + // 由于 gcd(a_i, a_j^3) 的范围是 [1,n],所以考虑枚举 gcd 结果 + // 考虑枚举 f(x) =count(gcd==x) 的结果数很难算,不妨考虑 g(x)=count(x|gcd) 的结果数 + // 比如 x = 7*7*7*11*11 + // 那么要求 a_i 必须是 x 的倍数 + // a_j 必须是 7*11 的倍数 + uint32_t n; + cin >> n; + OY::Eratosthenes::Sieve es(n); + std::vector cnt(n + 1); + std::vector arr(n + 1); + for (uint32_t i = 1; i <= n; i++) cin >> arr[i], cnt[arr[i]]++; + + // 做一下前缀和,令 cnt[x] 保存 x 的倍数的个数 + OY::MOBIUS::partial_sum_Dirichlet_multiple(n, cnt.data(), [&](uint32_t k) { return es.query_kth_prime(k); }); + + std::vector res(n + 1); + for (uint32_t x = 1; x <= n; x++) { + uint32_t cur = x, x2 = 1; + while (cur != 1) { + uint32_t p = es.query_smallest_factor(cur); + uint32_t _cnt = 0; + while (es.query_smallest_factor(cur) == p) _cnt++, cur /= p; + for (_cnt = (_cnt + 2) / 3; _cnt--;) x2 *= p; + } + res[x] = uint64_t(cnt[x]) * cnt[x2]; + } + + // 做一下前缀和,令 cnt[x] 保存 x 的倍数的个数 + OY::MOBIUS::adjacent_difference_Dirichlet_multiple(n, res.data(), [&](uint32_t k) { return es.query_kth_prime(k); }); + + uint64_t ans = 0; + for (uint32_t x = 1; x <= n; x++) + ans += res[x] * es.get_Euler_Phi(x); + cout << ans; +} uint32_t f[N + 1], g[N + 1]; void solve_mobius() { @@ -61,5 +100,6 @@ void solve_mobius() { } int main() { - solve_mobius(); + solve_inex(); + // solve_mobius(); } diff --git a/TEST/oj/nc_263978.cpp b/TEST/oj/nc_263978.cpp index 0552aca..4c8e808 100644 --- a/TEST/oj/nc_263978.cpp +++ b/TEST/oj/nc_263978.cpp @@ -7,6 +7,8 @@ [fsl 的背包](https://ac.nowcoder.com/acm/problem/263978) */ /** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/67159/G * 对每个物品判断其作为价值中位数的可能性 * 在比其价值低的所有物品里,选一半物品,且体积尽量小 * 在比其价值高的所有物品里,选一半物品,且体积尽量小 diff --git a/TEST/oj/nc_274793.cpp b/TEST/oj/nc_274793.cpp index be27855..ea13a9d 100644 --- a/TEST/oj/nc_274793.cpp +++ b/TEST/oj/nc_274793.cpp @@ -10,6 +10,8 @@ [记忆](https://ac.nowcoder.com/acm/problem/274793) */ /** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/84888/E * 本题的操作离线之后,只剩两种:+1 和 bitxor * 可以把树上 u 到 v 的路径分成两截 * u->lca 可以在 u 处将值插入 ReverseSegCounter,经历一路 +1 和 bitxor 之后,到 lca 处接收结果 diff --git a/TEST/oj/nc_274852.cpp b/TEST/oj/nc_274852.cpp index f410f75..2e7e6ee 100644 --- a/TEST/oj/nc_274852.cpp +++ b/TEST/oj/nc_274852.cpp @@ -8,6 +8,8 @@ [parent 树上启发式合并](https://ac.nowcoder.com/acm/problem/274852) */ /** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/85347/ * 注意本题题面坑。询问字符串的和是 300000 */ diff --git a/TEST/oj/nc_275139.cpp b/TEST/oj/nc_275139.cpp new file mode 100644 index 0000000..11c24a3 --- /dev/null +++ b/TEST/oj/nc_275139.cpp @@ -0,0 +1,100 @@ +#include "DS/AVL.h" +#include "DS/BIT01.h" +#include "DS/Splay.h" +#include "DS/StaticBufferWrapWithoutCollect.h" +#include "IO/FastIO.h" + +/* +[寿命修改](https://ac.nowcoder.com/acm/problem/275139) +*/ +/** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/85333/A + * 一个简单的做法就是用平衡树,维护序列的最小值,这样可以用二分找出挂掉的青蛙 + * 使用 avl 可以原地修改和查询 + * 使用 splay 需要分裂之后进行修改和查询,但是速度也很快 + */ + +template +struct NodeWrap { + using key_type = int64_t; + int64_t m_key, m_sum, m_min, m_inc; + void set(int key) { m_key = key; } + int get() const { return m_key; } + void add(int64_t x) { + m_key += x; + m_sum += x * ((Node *)(this))->m_sz; + m_min += x; + m_inc += x; + } + void pushup(Node *lc, Node *rc) { + m_sum = m_key + lc->m_sum + rc->m_sum; + m_min = m_key; + if (!lc->is_null()) m_min = std::min(m_min, lc->m_min); + if (!rc->is_null()) m_min = std::min(m_min, rc->m_min); + } + void pushdown(Node *lc, Node *rc) { + if (m_inc) { + if (!lc->is_null()) lc->add(m_inc); + if (!rc->is_null()) rc->add(m_inc); + m_inc = 0; + } + } +}; +using AVL = OY::AVL::Tree::type>; +// using AVL = OY::SPLAY::Tree::type>; +void solve_avl() { + uint32_t n, m; + cin >> n >> m; + OY::BIT01::Tree alive(n, [](auto...) { return 1; }); + auto read = [](auto...) { + uint32_t x; + cin >> x; + return x; + }; + AVL::buffer_type::data()[0].m_min = INT64_MAX; + auto S = AVL::from_mapping(n, read); + using node = AVL::node; + while (m--) { + char op; + uint32_t l, r; + cin >> op >> l >> r; + l--, r--; + uint32_t l0 = alive.presum(l - 1), r0 = alive.presum(r) - 1; + if (op == '1') { + int x; + cin >> x; + if (l0 == r0 + 1) continue; + auto node_call = [&](node *p) { p->m_key += x; }; + auto tree_call = [&](node *p) { p->add(x); }; + S.do_for_subtree_inplace(l0, r0, node_call, tree_call); + // S.do_for_subtree(l0, r0, tree_call); + while (!S.empty() && S.root()->m_min <= 0) { + struct MinGetter { + using value_type = int64_t; + int64_t operator()() const { return INT64_MAX; } + static void tree(int64_t &x, node *p) { x = std::min(x, p->m_min); } + static void node(int64_t &x, node *p) { x = std::min(x, p->m_key); } + }; + uint32_t pos = S.max_right(l0, [&](int64_t v) { return v > 0; }) + 1; + S.erase_by_rank(pos); + alive.reset(alive.kth(pos)); + } + } else { + if (l0 == r0 + 1) { + cout << "0\n"; + continue; + } + uint64_t sum{}; + auto node_call = [&](node *p) { sum += p->m_key; }; + auto tree_call = [&](node *p) { sum += p->m_sum; }; + S.do_for_subtree_inplace(l0, r0, node_call, tree_call); + // S.do_for_subtree(l0, r0, tree_call); + cout << sum << '\n'; + } + } +} + +int main() { + solve_avl(); +} \ No newline at end of file diff --git a/TEST/oj/nc_275989.cpp b/TEST/oj/nc_275989.cpp index 7fdb154..6b0b539 100644 --- a/TEST/oj/nc_275989.cpp +++ b/TEST/oj/nc_275989.cpp @@ -1,4 +1,5 @@ #include "DS/MonoAVL.h" +#include "DS/MonoPairHeap.h" #include "DS/MonoSplay.h" #include "IO/FastIO.h" @@ -8,24 +9,60 @@ /** * 上面的链接打不开 * 请换用这个链接 https://ac.nowcoder.com/acm/contest/86639/G - * 维护最小的 k 个元素之和 + * 要动态维护前 k 小和 + * 可以简单粗暴用平衡树维护半群信息 */ -using Tree = OY::VectorMonoSumAVL; -// using Tree = OY::VectorMonoSumSplay; -int main() { +static constexpr uint32_t N = 200000; +void solve_heap() { uint32_t n, k; uint64_t m; cin >> n >> m >> k; - Tree::_reserve(n + 1); - Tree S; - uint32_t ans{}; - for (uint32_t i = 0; i < n; i++) { + + using Heap = OY::VectorMonoSumPairHeap>; + Heap::_reserve(n + 1); + Heap h; + uint64_t tot = 0; + uint32_t ans = 0; + for (uint32_t i = 0; i != n; i++) { uint64_t x; cin >> x; - S.insert_by_comparator(x); - if (S.size() > k and S.query(0, S.size() - k - 1) >= m) break; - ans++; + tot += x; + h.push(x); + if (i >= k) h.pop(); + if (tot - h.query_all() < m) + ans = i + 1; + else + break; } cout << ans; +} + +uint64_t a[N]; +void solve_avl() { + uint32_t n, k; + uint64_t m; + cin >> n >> m >> k; + for (uint32_t i = 0; i != n; i++) cin >> a[i]; + + using AVL = OY::VectorMonoSumAVL; + // using AVL = OY::VectorMonoSumSplay; + AVL::_reserve(n + 1); + AVL tr; + uint32_t ans = 0; + for (uint32_t i = 0; i != n; i++) { + tr.insert_by_comparator(a[i]); + if (tr.size() <= k) + ans = i + 1; + else if (tr.query(0, tr.size() - k - 1) < m) + ans = i + 1; + else + break; + } + cout << ans; +} + +int main() { + solve_heap(); + // solve_avl(); } \ No newline at end of file diff --git a/TEST/oj/nc_279411.cpp b/TEST/oj/nc_279411.cpp new file mode 100644 index 0000000..0e784f8 --- /dev/null +++ b/TEST/oj/nc_279411.cpp @@ -0,0 +1,260 @@ +#include "DS/AdjDiff.h" +#include "DS/MaskRMQ.h" +#include "IO/FastIO.h" +#include "MISC/LazyMap.h" +#include "MISC/LazyUmap.h" +#include "TREE/AdjDiffTree.h" +#include "TREE/Centroid.h" +#include "TREE/FlatTree.h" +#include "TREE/RMQLCA.h" +#include "TREE/VirtualTree.h" + +#include + +/* +[k - 路径(hard vension)](https://ac.nowcoder.com/acm/problem/279411) +*/ +/** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/91355/G + * 一个很自然的想法,既然路径两端数字一样,就把数字一样的点取出来两两配对 + * 需要用到虚树 + * + * 当然,点分治,甚至纯粹的启发式合并都是可行的 + */ + +static constexpr uint32_t MAXN = 200000; +uint32_t c[MAXN], fa[MAXN]; +int w[MAXN]; +using hash_type = uint32_t; +hash_type ha[MAXN], ha_of[MAXN]; +OY::AdjSumTable A; + +std::vector appear[MAXN]; +void solve_vtree() { + uint32_t n; + cin >> n; + for (uint32_t i = 0; i != n; i++) appear[i].clear(); + for (uint32_t i = 0; i != n; i++) { + cin >> c[i]; + appear[--c[i]].push_back(i); + } + for (uint32_t i = 0; i != n; i++) cin >> w[i]; + OY::FlatTree::Tree S(n); + for (uint32_t i = 1; i != n; i++) { + uint32_t a, b; + cin >> a >> b; + S.add_edge(a - 1, b - 1); + } + S.prepare(), S.set_root(0); + // 求一下 fa + auto pre_work = [](uint32_t a, uint32_t p) { fa[a] = p; }; + S.tree_dp_vertex(0, pre_work, {}, {}); + // adj1 维护哈希 + OY::AdjSumTreeTable adj1(&S, [](auto i) { return ha_of[i] = ha[c[i]]; }); + adj1.switch_to_presum_downward(); + // adj2 维护权值 + OY::AdjSumTreeTable adj2(&S, [](auto i) { return w[i]; }); + adj2.switch_to_presum_downward(); + // 准备一个 rmqlca 查询 lca + OY::RMQLCA::Table> rmq(&S); + // 枚举两端的数字 + for (uint32_t v = 0; v != n; v++) { + if (appear[v].size() <= 1) { + cout << "-1 "; + continue; + } + using LazyMap = OY::LazyKeyAddMappedAddMap; + // using LazyMap = OY::LazyKeyAddMappedAddUmap; + // v 的权值会被计算两次,因为有两端 + auto &&target = A.query(0, v) + ha[v]; + int64_t ans = INT64_MIN / 4; + + static LazyMap maps[MAXN]; + static bool vis[MAXN]; + static std::vector rec; + OY::VTREE::VirtualTree<1000000>::solve_rmqlca(appear[v].begin(), appear[v].end(), rmq, [&](uint32_t a, uint32_t p) { + if (!vis[a]) { + vis[a] = true, rec.push_back(a); + if (c[a] == v) maps[a].insert_or_assign(ha_of[a], w[a]); + } + if (!vis[p]) { + vis[p] = true, rec.push_back(p); + if (c[p] == v) maps[p].insert_or_assign(ha_of[p], w[p]); + } + + auto &mp_a = maps[a], &mp_p = maps[p]; + mp_a.globally_add({adj1.query_path(fa[a], p, fa[p]), adj2.query_path(fa[a], p, fa[p])}); + if (mp_a.size() > mp_p.size()) std::swap(mp_a, mp_p); + // ha_of[p] 会被计数两次 + mp_a.enumerate([&](auto _k, auto _v) { + auto get = mp_p.get(target + ha_of[p] - _k, INT64_MIN / 2); + ans = std::max(ans, _v + get - w[p]); + }); + // mp_a 被合并 + mp_a.enumerate([&](auto _k, auto _v) { + mp_p.insert_or_modify(_k, [&](auto &old_v, bool flag) { + old_v = flag ? _v : std::max(old_v, _v); + }); + }); + }); + for (auto x : rec) vis[x] = false, maps[x].clear(); + rec.clear(); + if (ans <= INT64_MIN / 4) + cout << "-1 "; + else + cout << ans << ' '; + } + cout << endl; +} + +void solve_merge() { + uint32_t n; + cin >> n; + for (uint32_t i = 0; i != n; i++) cin >> c[i], c[i]--; + for (uint32_t i = 0; i != n; i++) cin >> w[i]; + OY::FlatTree::Tree S(n); + for (uint32_t i = 1; i != n; i++) { + uint32_t a, b; + cin >> a >> b; + S.add_edge(a - 1, b - 1); + } + S.prepare(), S.set_root(0); + for (uint32_t i = 0; i != n; i++) ha_of[i] = ha[c[i]]; + + std::vector ans(n, INT64_MIN / 4); + using Map0 = OY::LazyKeyAddMappedAddMap; + // using Map0 = OY::LazyKeyAddMappedAddUmap; + using Map = OY::LAZYMAP::Table; + // using Map = OY::LAZYUMAP::Table; + std::vector maps(n); + auto pre_work = [&](uint32_t a, uint32_t p) { + maps[a].get(c[a]).get(ha_of[a]) = w[a]; + }; + auto report = [&](uint32_t a, uint32_t to) { + // 把各个结点到 to 的哈希和权值都加上 a 的影响 + // 所以现在 maps[to] 保存的是各个结点到 a 的哈希和权值 + // 当然 maps[a] 保存的也是一样,所以二者是一样的 + maps[to].globally_add({ha_of[a], w[a]}); + if (maps[a].size() < maps[to].size()) std::swap(maps[a], maps[to]); + maps[to].enumerate([&](auto _c, auto &sub_map) { + auto it = maps[a].find(_c); + if (it == maps[a].end()) + maps[a].insert_or_assign(_c, std::move(sub_map)); + else { + auto &sub_map2 = it->second.m_val; + if (sub_map2.size() < sub_map.size()) std::swap(sub_map, sub_map2); + // 子树根会被多算一次 + auto target = A.query(0, _c) + ha[_c] + ha_of[a]; + sub_map.enumerate([&](auto _ha, auto _w) { + ans[_c] = std::max(ans[_c], _w + sub_map2.get(target - _ha, INT64_MIN / 2) - w[a]); + }); + // 合并二级 map + sub_map.enumerate([&](auto _ha, auto _w) { + sub_map2.insert_or_modify(_ha, [&](auto &old, bool flag) { + old = flag ? _w : std::max(old, _w); + }); + }); + } + }); + }; + S.tree_dp_vertex(0, pre_work, report, {}); + + for (uint32_t v = 0; v != n; v++) cout << (ans[v] <= INT64_MIN / 4 ? -1 : ans[v]) << " \n"[v == n - 1]; +} + +void solve_centroid() { + uint32_t n; + cin >> n; + for (uint32_t i = 0; i != n; i++) cin >> c[i], c[i]--; + for (uint32_t i = 0; i != n; i++) cin >> w[i]; + OY::FlatTree::Tree S(n); + for (uint32_t i = 1; i != n; i++) { + uint32_t a, b; + cin >> a >> b; + S.add_edge(a - 1, b - 1); + } + S.prepare(), S.set_root(0); + for (uint32_t i = 0; i != n; i++) ha_of[i] = ha[c[i]]; + + // 在点分树模板里,传递回调函数 + // 由于本题在建好点分树之后就是一个 dp,所以其实可以不用真正地建树,在找到边的时候直接转移即可 + std::vector ans(n, INT64_MIN / 4); + auto chmax = [](auto &x, auto y) { if (y > x) x = y; }; + static bool blocked[MAXN]; + + auto pre_work = [&](uint32_t root) { + blocked[root] = true; + using Map = std::map; + static std::vector maps(MAXN); + static std::vector rec; + // 以 root 为连接点,搜索各种拼接的路径 + maps[c[root]][ha_of[root]] = w[root], rec.push_back(c[root]); + S.do_for_each_adj_vertex(root, [&](uint32_t to) { + if (blocked[to]) return; + static std::vector to_maps(MAXN); + static std::vector to_rec; + // 遇到 blocked 就剪枝的 dfs + auto dfs = [&](auto &&self, uint32_t a, uint32_t p, hash_type _ha, int64_t _w) { + if (blocked[a]) return; + _ha += ha_of[a], _w += w[a]; + auto &mp = to_maps[c[a]]; + if (mp.empty()) to_rec.push_back(c[a]); + auto res = mp.insert({_ha, _w}); + if (!res.second) chmax(res.first->second, _w); + S.do_for_each_adj_vertex(a, [&](uint32_t b) { + if (b != p) self(self, b, a, _ha, _w); + }); + }; + dfs(dfs, to, root, ha_of[root], w[root]); + // 合并一下 + if (rec.size() < to_rec.size()) { + std::swap(maps, to_maps); + std::swap(rec, to_rec); + } + for (auto v : to_rec) { + auto &mp = to_maps[v]; + auto &mp2 = maps[v]; + if (mp2.empty()) { + mp2 = std::move(mp), rec.push_back(v); + continue; + } + if (mp2.size() < mp.size()) mp2.swap(mp); + auto target = A.query(0, v) + ha[v] + ha_of[root]; + // 找匹配 + for (auto &[_ha, _w] : mp) { + auto find = mp2.find(target - _ha); + if (find != mp2.end()) chmax(ans[v], _w + find->second - w[root]); + } + // 合并二级 map + for (auto &[_ha, _w] : mp) { + auto res = mp2.insert({_ha, _w}); + if (!res.second) chmax(res.first->second, _w); + } + Map().swap(mp); + } + to_rec.clear(); + }); + for (auto v : rec) Map().swap(maps[v]); + rec.clear(); + }; + auto after_work = [&](uint32_t root) { blocked[root] = false; }; + OY::Centroid::CentroidDecomposition::solve(S, pre_work, {}, after_work); + + for (uint32_t v = 0; v != n; v++) cout << (ans[v] <= INT64_MIN / 4 ? -1 : ans[v]) << " \n"[v == n - 1]; +} + +int main() { + A.resize(MAXN, [](auto i) { + static std::mt19937 rng; + return ha[i] = rng(); + }); + A.switch_to_presum(); + uint32_t t; + cin >> t; + while (t--) { + solve_vtree(); + // solve_merge(); + // solve_centroid(); + } +} \ No newline at end of file diff --git a/TEST/oj/nc_281352.cpp b/TEST/oj/nc_281352.cpp new file mode 100644 index 0000000..86e5164 --- /dev/null +++ b/TEST/oj/nc_281352.cpp @@ -0,0 +1,60 @@ +#include "DS/MaskRMQ.h" +#include "IO/FastIO.h" +#include "TREE/AdjDiffTree.h" +#include "TREE/FlatTree.h" +#include "TREE/RMQLCA.h" +#include "TREE/TreeTransfer.h" + +#include + +/* +[小红的树上路径查询(hard)](https://ac.nowcoder.com/acm/problem/281352) +*/ +/** + * 上面的链接打不开 + * 请换用这个链接 https://ac.nowcoder.com/acm/contest/92662/G + * 首先要会算,对于某一个结点,其它结点到它的距离和 + * 然后令其为 lca ,在这个结点下方追加结点,添加贡献 + */ + +int main() { + uint32_t n, q; + cin >> n >> q; + OY::FlatTree::Tree S(n); + for (uint32_t i = 1; i != n; i++) { + uint32_t a, b; + cin >> a >> b; + S.add_edge(a - 1, b - 1); + } + S.prepare(), S.set_root(0); + struct Info { + uint32_t size, fa; + uint64_t down_sum, up_sum; + }; + auto mapping = [](uint32_t i) -> Info { return {1}; }; + auto merge = [](Info &dp_a, Info dp_son, uint32_t a, uint32_t son, auto...) { + dp_a.size += dp_son.size; + dp_a.down_sum += dp_son.down_sum + dp_son.size; + }; + auto exclude = [&](Info &dp_a, Info dp_fa, uint32_t a, uint32_t fa, auto...) { + if (~(dp_a.fa = fa)) { + dp_a.up_sum = dp_fa.up_sum + (n - dp_fa.size); + dp_a.up_sum += dp_fa.down_sum - (dp_a.down_sum + dp_a.size) + (dp_fa.size - dp_a.size); + } + }; + auto dp = OY::TreeTransfer::solve(S, mapping, merge, exclude); + OY::AdjSumTreeTable A(&S, [&](auto i) { + return dp[i].size; + }); + A.switch_to_presum_downward(); + OY::RMQLCA::Table> R(&S); + while (q--) { + uint32_t a, b; + cin >> a >> b; + uint32_t lca = R.calc(--a, --b); + uint64_t ans = dp[lca].up_sum + dp[lca].down_sum + dp[lca].size * 2; + ans -= A.query_path(a, lca, dp[lca].fa); + ans -= A.query_path(b, lca, dp[lca].fa); + cout << ans << endl; + } +} \ No newline at end of file diff --git a/TEST/oj/ysp_874.cpp b/TEST/oj/ysp_874.cpp index d7ca97c..ac4ba4b 100644 --- a/TEST/oj/ysp_874.cpp +++ b/TEST/oj/ysp_874.cpp @@ -16,7 +16,7 @@ int main() { cin >> x; return x; }; - OY::MMHEAP::Heap H(n, read); + OY::MMHeap::Heap H(n, read); for (uint32_t i = 0; i != m; i++) { char op; cin >> op; diff --git a/TREE/AdjDiffTree.md b/TREE/AdjDiffTree.md index 78be7a5..32a306e 100644 --- a/TREE/AdjDiffTree.md +++ b/TREE/AdjDiffTree.md @@ -7,6 +7,8 @@ 1. [P3128 [USACO15DEC] Max Flow P](https://www.luogu.com.cn/problem/P3128) 2. [P3258 [JLOI2014] 松鼠的新家](https://www.luogu.com.cn/problem/P3258) 3. [剖分](https://ac.nowcoder.com/acm/problem/244121) +4. [k - 路径(hard vension)](https://ac.nowcoder.com/acm/problem/279411) +5. [小红的树上路径查询(hard)](https://ac.nowcoder.com/acm/problem/281352) ### 二、模板功能 diff --git a/TREE/Centroid.md b/TREE/Centroid.md index d86a02e..80f60f3 100644 --- a/TREE/Centroid.md +++ b/TREE/Centroid.md @@ -8,8 +8,9 @@ 2. [P4178 Tree](https://www.luogu.com.cn/problem/P4178) 3. [P5043 【模板】树同构([BJOI2015]树的同构)](https://www.luogu.com.cn/problem/P5043) 4. [U104609 【模板】树的重心](https://www.luogu.com.cn/problem/U104609) -5. [#763. 树哈希](https://uoj.ac/problem/763) -6. [Rooted Tree Isomorphism Classification](https://judge.yosupo.jp/problem/rooted_tree_isomorphism_classification)(https://github.com/yosupo06/library-checker-problems/issues/796) +5. [k - 路径(hard vension)](https://ac.nowcoder.com/acm/problem/279411) +6. [#763. 树哈希](https://uoj.ac/problem/763) +7. [Rooted Tree Isomorphism Classification](https://judge.yosupo.jp/problem/rooted_tree_isomorphism_classification)(https://github.com/yosupo06/library-checker-problems/issues/796) ### 二、模板功能 diff --git a/TREE/RMQLCA.md b/TREE/RMQLCA.md index aae3628..04dec66 100644 --- a/TREE/RMQLCA.md +++ b/TREE/RMQLCA.md @@ -10,7 +10,9 @@ 4. [P3379 【模板】最近公共祖先(LCA)](https://www.luogu.com.cn/problem/P3379) 5. [P4103 [HEOI2014] 大工程](https://www.luogu.com.cn/problem/P4103) 6. [P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并](https://www.luogu.com.cn/problem/P4556) -7. [Lowest Common Ancestor](https://judge.yosupo.jp/problem/lca)(https://github.com/yosupo06/library-checker-problems/issues/35) +7. [k - 路径(hard vension)](https://ac.nowcoder.com/acm/problem/279411) +8. [小红的树上路径查询(hard)](https://ac.nowcoder.com/acm/problem/281352) +9. [Lowest Common Ancestor](https://judge.yosupo.jp/problem/lca)(https://github.com/yosupo06/library-checker-problems/issues/35) ### 二、模板功能 diff --git a/TREE/TreeTransfer.md b/TREE/TreeTransfer.md index dbe1ef3..852ef19 100644 --- a/TREE/TreeTransfer.md +++ b/TREE/TreeTransfer.md @@ -9,7 +9,8 @@ 3. [P3304 [SDOI2013] 直径](https://www.luogu.com.cn/problem/P3304) 4. [P3478 [POI2008] STA-Station](https://www.luogu.com.cn/problem/P3478) 5. [P10842 【MX-J2-T3】Piggy and Trees](https://www.luogu.com.cn/problem/P10842) -6. [Tree Path Composite Sum](https://judge.yosupo.jp/problem/tree_path_composite_sum)(https://github.com/yosupo06/library-checker-problems/issues/861) +6. [小红的树上路径查询(hard)](https://ac.nowcoder.com/acm/problem/281352) +7. [Tree Path Composite Sum](https://judge.yosupo.jp/problem/tree_path_composite_sum)(https://github.com/yosupo06/library-checker-problems/issues/861) ### 二、模板功能 diff --git a/TREE/VirtualTree.h b/TREE/VirtualTree.h index cab5a30..a8db448 100644 --- a/TREE/VirtualTree.h +++ b/TREE/VirtualTree.h @@ -50,13 +50,15 @@ namespace OY { } template static void solve_rmqlca(Iterator first, Iterator last, RMQLCA &&rmqlca, Callback &&call) { - solve( - first, last, [&](size_type a) { return rmqlca.m_dfn[a]; }, [&](size_type a, size_type b) { return rmqlca.calc(a, b); }, call); + auto dfn_getter = [&](size_type a) { return rmqlca.m_dfn[a]; }; + auto lca_getter = [&](size_type a, size_type b) { return rmqlca.calc(a, b); }; + solve(first, last, dfn_getter, lca_getter, call); } template static void solve_hld(Iterator first, Iterator last, HLD &&hld, Callback &&call) { - solve( - first, last, [&](size_type a) { return hld.m_info[a].m_dfn; }, [&](size_type a, size_type b) { return hld.calc(a, b); }, call); + auto dfn_getter = [&](size_type a) { return hld.m_info[a].m_dfn; }; + auto lca_getter = [&](size_type a, size_type b) { return hld.calc(a, b); }; + solve(first, last, dfn_getter, lca_getter, call); } }; template diff --git a/TREE/VirtualTree.md b/TREE/VirtualTree.md index b46be51..cac1c49 100644 --- a/TREE/VirtualTree.md +++ b/TREE/VirtualTree.md @@ -6,6 +6,7 @@ 1. [P2495 [SDOI2011] 消耗战](https://www.luogu.com.cn/problem/P2495) 2. [P4103 [HEOI2014] 大工程](https://www.luogu.com.cn/problem/P4103) +3. [k - 路径(hard vension)](https://ac.nowcoder.com/acm/problem/279411) ### 二、模板功能