Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_mo_algorithm.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
64# ifndef TPL_MO_ALGORITHM_H
65# define TPL_MO_ALGORITHM_H
66
67# include <algorithm>
68# include <cmath>
69# include <concepts>
70# include <type_traits>
71# include <utility>
72# include <tpl_array.H>
73# include <tpl_dynMapOhash.H>
74# include <tpl_dynList.H>
75# include <ah-errors.H>
76
77namespace Aleph
78{
82 struct Mo_Query
83 {
84 size_t l;
85 size_t r;
86 size_t id;
87 };
88
102 template <typename P, typename T>
103 concept MoPolicy = requires(P & p, const Array<T> & data,
104 size_t idx, size_t n)
105 {
106 typename P::answer_type;
107 { p.init(data, n) };
108 { p.add(data, idx) };
109 { p.remove(data, idx) };
110 { p.answer() } -> std::convertible_to<typename P::answer_type>;
111 };
112
113 // ================================================================
114 // Gen_Mo_Algorithm — Offline range queries via Mo's algorithm
115 // ================================================================
116
139 template <typename T, class Policy>
140 requires MoPolicy<Policy, T>
142 {
144 mutable Policy pol_;
145
146 public:
147 using Item_Type = T;
148 using answer_type = typename Policy::answer_type;
149
161 Gen_Mo_Algorithm(std::initializer_list<T> il, Policy p = Policy())
162 : data_(il), pol_(std::move(p))
163 {}
164
177 : data_(arr), pol_(std::move(p))
178 {}
179
192 : data_(std::move(arr)), pol_(std::move(p))
193 {}
194
207 : data_(Array<T>::create(lst.size())), pol_(std::move(p))
208 {
209 size_t i = 0;
210 for (auto it = lst.get_it(); it.has_curr(); it.next_ne())
211 data_(i++) = it.get_curr();
212 }
213
215
219
221
225
228 {
229 return data_.size();
230 }
231
233 [[nodiscard]] constexpr bool is_empty() const noexcept
234 {
235 return data_.size() == 0;
236 }
237
239 void swap(Gen_Mo_Algorithm & other) noexcept(
240 noexcept(std::swap(std::declval<Policy &>(), std::declval<Policy &>())))
241 {
242 data_.swap(other.data_);
243 std::swap(pol_, other.pol_);
244 }
245
265 Array<answer_type> solve(const Array<std::pair<size_t, size_t>> & ranges) const
266 {
268 for (size_t i = 0; i < ranges.size(); ++i)
269 queries(i) = {ranges(i).first, ranges(i).second, i};
270 return solve(queries);
271 }
272
289 Array<answer_type> solve(const std::initializer_list<std::pair<size_t, size_t>> il) const
290 {
292 return solve(ranges);
293 }
294
318 {
319 const size_t n = data_.size();
320 const size_t q = queries.size();
321
322 if (q == 0)
323 return Array<answer_type>();
324
325 // Validate all queries
326 for (size_t i = 0; i < q; ++i)
327 {
329 << "Gen_Mo_Algorithm::solve: query " << i
330 << " r=" << queries(i).r << " >= n=" << n;
332 << "Gen_Mo_Algorithm::solve: query " << i
333 << " l=" << queries(i).l << " > r=" << queries(i).r;
335 << "Gen_Mo_Algorithm::solve: query " << i
336 << " id=" << queries(i).id << " >= q=" << q;
337 }
338
339 const size_t block = std::max<size_t>(1, static_cast<size_t>(std::sqrt(static_cast<double>(n))));
340
341 // Snake sort: primary by l/block, secondary alternating by r
342 std::sort(&queries(0), &queries(0) + q,
343 [block](const Mo_Query & a, const Mo_Query & b)
344 {
345 const size_t ba = a.l / block;
346 const size_t bb = b.l / block;
347 if (ba != bb)
348 return ba < bb;
349 // Even blocks: r ascending; odd blocks: r descending
350 return (ba & 1) ? (a.r > b.r) : (a.r < b.r);
351 });
352
353 pol_.init(data_, n);
354
356
357 // Initialise a window to the first query
358 size_t cur_l = queries(0).l;
359 size_t cur_r = queries(0).l;
360 pol_.add(data_, cur_l);
361
362 // Expand to the first query's right endpoint
363 while (cur_r < queries(0).r)
364 pol_.add(data_, ++cur_r);
365
366 answers(queries(0).id) = pol_.answer();
367
368 // Sweep remaining queries
369 for (size_t i = 1; i < q; ++i)
370 {
371 const size_t ql = queries(i).l;
372 const size_t qr = queries(i).r;
373
374 // Expand right
375 while (cur_r < qr)
376 pol_.add(data_, ++cur_r);
377
378 // Expand left
379 while (cur_l > ql)
380 pol_.add(data_, --cur_l);
381
382 // Shrink right
383 while (cur_r > qr)
384 pol_.remove(data_, cur_r--);
385
386 // Shrink left
387 while (cur_l < ql)
388 pol_.remove(data_, cur_l++);
389
390 answers(queries(i).id) = pol_.answer();
391 }
392
393 return answers;
394 }
395 };
396
397 // ================================================================
398 // Built-in policies
399 // ================================================================
400
410 template <typename T>
412 {
413 using answer_type = size_t;
414
416 size_t distinct = 0;
417
418 void init(const Array<T> &, size_t)
419 {
421 freq.swap(tmp);
422 distinct = 0;
423 }
424
425 void add(const Array<T> & data, size_t idx)
426 {
427 if (++freq[data(idx)] == 1)
428 ++distinct;
429 }
430
431 void remove(const Array<T> & data, size_t idx)
432 {
433 if (--freq[data(idx)] == 0)
434 --distinct;
435 }
436
437 [[nodiscard]] answer_type answer() const { return distinct; }
438 };
439
451 template <typename T>
453 {
454 using answer_type = long long;
455
457 long long sum = 0;
458
459 void init(const Array<T> &, size_t)
460 {
462 cnt.swap(tmp);
463 sum = 0;
464 }
465
466 void add(const Array<T> & data, size_t idx)
467 {
468 const auto x = static_cast<long long>(data(idx));
469 sum += (2 * cnt[data(idx)] + 1) * x;
470 ++cnt[data(idx)];
471 }
472
473 void remove(const Array<T> & data, size_t idx)
474 {
475 const auto x = static_cast<long long>(data(idx));
476 --cnt[data(idx)];
477 sum -= (2 * cnt[data(idx)] + 1) * x;
478 }
479
480 [[nodiscard]] answer_type answer() const { return sum; }
481 };
482
497 template <typename T>
499 {
500 using answer_type = std::pair<size_t, T>;
501
504 size_t max_freq = 0;
506
507 void init(const Array<T> &, size_t)
508 {
511 freq.swap(tmp_freq);
512 cnt_of_cnt.swap(tmp_cnt);
513 max_freq = 0;
514 mode_val = T();
515 }
516
517 void add(const Array<T> & data, size_t idx)
518 {
519 const T & x = data(idx);
520 const size_t old_f = freq[x];
521 if (old_f > 0)
522 --cnt_of_cnt[old_f];
523 ++freq[x];
524 ++cnt_of_cnt[old_f + 1];
525 if (old_f + 1 > max_freq)
526 {
527 max_freq = old_f + 1;
528 mode_val = x;
529 }
530 }
531
532 void remove(const Array<T> & data, size_t idx)
533 {
534 const T & x = data(idx);
535 const size_t old_f = freq[x];
536 --cnt_of_cnt[old_f];
537 if (old_f > 1)
538 ++cnt_of_cnt[old_f - 1];
539 --freq[x];
540 if (old_f == max_freq and cnt_of_cnt[old_f] == 0)
541 {
542 --max_freq;
543 // Find a value with the new max frequency; if max_freq is 0,
544 // the window is empty.
545 if (max_freq > 0)
546 {
547 for (typename MapOLhash<T, size_t>::Iterator it(freq);
548 it.has_curr(); it.next_ne())
549 if (it.get_curr().second == max_freq)
550 {
551 mode_val = it.get_curr().first;
552 break;
553 }
554 }
555 }
556 else if (old_f == max_freq and x == mode_val)
557 {
558 // x was the mode, but other elements still share max_freq;
559 // pick any one of them.
560 for (typename MapOLhash<T, size_t>::Iterator it(freq);
561 it.has_curr(); it.next_ne())
562 if (it.get_curr().second == max_freq)
563 {
564 mode_val = it.get_curr().first;
565 break;
566 }
567 }
568 }
569
570 answer_type answer() const { return {max_freq, mode_val}; }
571 };
572
573 // ================================================================
574 // Convenient typedefs
575 // ================================================================
576
581 template <typename T>
583
588 template <typename T>
590
595 template <typename T>
597} // namespace Aleph
598
599# endif // TPL_MO_ALGORITHM_H
Exception handling system with formatted messages for Aleph-w.
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:579
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:194
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
Offline range query engine using Mo's algorithm.
Array< answer_type > solve(Array< Mo_Query > queries) const
Core solver: answer all Mo_Query objects.
Array< answer_type > solve(const std::initializer_list< std::pair< size_t, size_t > > il) const
Solve queries given as (l, r) pairs (initializer list).
Gen_Mo_Algorithm(Gen_Mo_Algorithm &&) noexcept(std::is_nothrow_move_constructible_v< Array< T > > and std::is_nothrow_move_constructible_v< Policy >)=default
Gen_Mo_Algorithm(Array< T > and arr, Policy p=Policy())
Construct from an Array<T> (move).
Gen_Mo_Algorithm(const DynList< T > &lst, Policy p=Policy())
Construct from a DynList<T>.
constexpr bool is_empty() const noexcept
True if the data array is empty.
Array< answer_type > solve(const Array< std::pair< size_t, size_t > > &ranges) const
Solve queries given as (l, r) pairs.
void swap(Gen_Mo_Algorithm &other) noexcept(noexcept(std::swap(std::declval< Policy & >(), std::declval< Policy & >())))
Swap with other in O(1).
Gen_Mo_Algorithm(std::initializer_list< T > il, Policy p=Policy())
Construct from an initializer list.
constexpr size_t size() const noexcept
Number of elements in the data array.
typename Policy::answer_type answer_type
Gen_Mo_Algorithm(const Array< T > &arr, Policy p=Policy())
Construct from an Array<T>.
Gen_Mo_Algorithm(const Gen_Mo_Algorithm &)=default
Concept constraining a policy for Mo's algorithm.
pair< size_t, string > P
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
STL namespace.
Policy: count distinct elements in a range.
void add(const Array< T > &data, size_t idx)
void remove(const Array< T > &data, size_t idx)
MapOLhash< T, size_t > freq
void init(const Array< T > &, size_t)
Open addressing hash map using linear probing.
typename Base::Iterator Iterator
Iterator type for traversing the map.
A query for Mo's algorithm: inclusive range [l, r] with id.
size_t id
Original query index (for reordering answers).
size_t l
Left endpoint (inclusive, 0-based).
size_t r
Right endpoint (inclusive, 0-based).
Policy: "powerful array" sum = sum(cnt[x]^2 * x).
void init(const Array< T > &, size_t)
void remove(const Array< T > &data, size_t idx)
void add(const Array< T > &data, size_t idx)
MapOLhash< T, long long > cnt
Policy: range mode (most frequent element).
void remove(const Array< T > &data, size_t idx)
std::pair< size_t, T > answer_type
MapOLhash< T, size_t > freq
void init(const Array< T > &, size_t)
answer_type answer() const
void add(const Array< T > &data, size_t idx)
MapOLhash< size_t, size_t > cnt_of_cnt
gsl_rng * r
Dynamic array container with automatic resizing.
Alias for htlist.H (DynList implementation).
Dynamic map with open hashing.
DynList< int > l