Range Majority
(ds/range_query/range_majority.hpp)
- View this file on GitHub
- Last update: 2026-07-21 22:01:00+09:00
- Include:
#include "ds/range_query/range_majority.hpp"
Overview
RangeMajority<T> maintains a fixed-length array under point assignments
and finds the strict majority of any nonempty half-open range [left, right).
A strict majority is a value occurring more than (right - left) / 2 times. It
is necessarily unique. The query returns std::nullopt when no strict majority
exists.
Each segment-tree node stores the result of Boyer–Moore cancellation. A strict majority survives every cancellation of two unequal values, so merging the nodes covering a query produces the only possible candidate. An order-statistics set of current positions for each value verifies the candidate’s exact frequency and supports point assignments.
The algorithm is deterministic.
Requirements and Behavior
T must be copyable and provide a strict weak ordering through operator<.
Construction does not modify the input vector. set accepts values that were
not present during construction.
Let $D$ be the number of distinct values seen by the constructor and all calls
to set. The structure uses $O(N + D)$ memory. It retains bookkeeping for a
value after its final occurrence is replaced.
Queries use zero-based indices and require
0 <= left < right <= size(). Point assignments require
0 <= index < size() and mutate the structure. Both query methods are const.
Interface
| Method | Exact signature | Description | Complexity |
|---|---|---|---|
| Constructor | RangeMajority() |
Constructs an empty structure. | $O(1)$ |
| Constructor | explicit RangeMajority(const std::vector<T>& values) |
Preprocesses values. |
$O(N\log N)$ time |
size |
int size() const |
Returns the array length. | $O(1)$ |
empty |
bool empty() const |
Returns whether the array is empty. | $O(1)$ |
set |
void set(int index, T value) |
Assigns value to position index. |
Amortized $O(\log N + \log D)$ |
query |
std::optional<T> query(int left, int right) const |
Returns the strict majority of [left, right), or std::nullopt. |
$O(\log N)$ |
majority |
std::optional<T> majority(int left, int right) const |
Alias of query. |
$O(\log N)$ |
result_type is an alias of std::optional<T>.
Example
#include "ds/range_query/range_majority.hpp"
#include <iostream>
#include <vector>
int main() {
std::vector<int> values = {2, 1, 2, 2, 3, 3};
m1une::ds::RangeMajority<int> majority(values);
auto first = majority.query(0, 4);
if (first) std::cout << *first << '\n'; // 2
majority.set(1, 3);
auto second = majority.query(1, 6);
if (second) std::cout << *second << '\n'; // 3
}
Depends on
Verified with
Code
#ifndef M1UNE_DS_RANGE_QUERY_RANGE_MAJORITY_HPP
#define M1UNE_DS_RANGE_QUERY_RANGE_MAJORITY_HPP 1
#include "../bst/ordered_set.hpp"
#include <cassert>
#include <map>
#include <optional>
#include <utility>
#include <vector>
namespace m1une {
namespace ds {
// Strict-majority queries and point assignments in O(log N) time.
template <class T>
struct RangeMajority {
using result_type = std::optional<T>;
private:
struct Vote {
int candidate = -1;
int balance = 0;
};
int _n;
int _tree_size;
std::vector<T> _values;
std::map<T, int> _ranks;
std::vector<int> _current;
std::vector<OrderedSet<int>> _positions;
std::vector<Vote> _tree;
int register_value(T value) {
int rank = int(_values.size());
auto [iterator, inserted] = _ranks.emplace(std::move(value), rank);
if (inserted) {
_values.push_back(iterator->first);
_positions.emplace_back();
}
return iterator->second;
}
static Vote combine(Vote first, Vote second) {
if (first.balance == 0) return second;
if (second.balance == 0) return first;
if (first.candidate == second.candidate) {
return Vote{first.candidate, first.balance + second.balance};
}
if (first.balance > second.balance) {
return Vote{first.candidate, first.balance - second.balance};
}
if (second.balance > first.balance) {
return Vote{second.candidate, second.balance - first.balance};
}
return Vote();
}
Vote range_vote(int left, int right) const {
Vote left_vote;
Vote right_vote;
left += _tree_size;
right += _tree_size;
while (left < right) {
if (left & 1) left_vote = combine(left_vote, _tree[left++]);
if (right & 1) right_vote = combine(_tree[--right], right_vote);
left >>= 1;
right >>= 1;
}
return combine(left_vote, right_vote);
}
public:
RangeMajority() : _n(0), _tree_size(1), _tree(2) {}
explicit RangeMajority(const std::vector<T>& values)
: _n(int(values.size())),
_tree_size(1),
_current(values.size()) {
_values.reserve(values.size());
_positions.reserve(values.size());
while (_tree_size < _n) _tree_size <<= 1;
_tree.assign(2 * _tree_size, Vote());
for (int index = 0; index < _n; index++) {
int rank = register_value(values[index]);
_current[index] = rank;
_positions[rank].insert(index);
_tree[_tree_size + index] = Vote{rank, 1};
}
for (int node = _tree_size - 1; node > 0; node--) {
_tree[node] = combine(_tree[node << 1], _tree[(node << 1) | 1]);
}
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
// Assigns value to one position. Previously unseen values are supported.
void set(int index, T value) {
assert(0 <= index && index < _n);
int rank = register_value(std::move(value));
int previous_rank = _current[index];
if (rank == previous_rank) return;
[[maybe_unused]] bool erased =
_positions[previous_rank].erase(index);
[[maybe_unused]] bool inserted = _positions[rank].insert(index);
assert(erased && inserted);
_current[index] = rank;
int node = _tree_size + index;
_tree[node] = Vote{rank, 1};
while ((node >>= 1) != 0) {
_tree[node] = combine(_tree[node << 1], _tree[(node << 1) | 1]);
}
}
// Returns the unique value occurring more than half the time in
// [left, right), or nullopt when no such value exists.
result_type query(int left, int right) const {
assert(0 <= left && left < right && right <= _n);
Vote vote = range_vote(left, right);
if (vote.balance == 0) return std::nullopt;
const OrderedSet<int>& positions = _positions[vote.candidate];
int frequency =
positions.order_of_key(right) - positions.order_of_key(left);
if (2LL * frequency <= right - left) return std::nullopt;
return _values[vote.candidate];
}
result_type majority(int left, int right) const {
return query(left, right);
}
};
} // namespace ds
} // namespace m1une
#endif // M1UNE_DS_RANGE_QUERY_RANGE_MAJORITY_HPP#line 1 "ds/range_query/range_majority.hpp"
#line 1 "ds/bst/ordered_set.hpp"
#include <cassert>
#include <functional>
#include <initializer_list>
#include <memory>
#include <utility>
#include <vector>
namespace m1une {
namespace ds {
template <typename T, typename Compare = std::less<T>>
struct OrderedSet {
private:
struct Node {
T key;
int size;
Node* l;
Node* r;
explicit Node(T value)
: key(std::move(value)), size(1), l(nullptr), r(nullptr) {}
};
static constexpr int pool_block_size = 1 << 15;
struct Pool {
std::vector<std::vector<Node>> blocks;
std::vector<Node*> free_nodes;
template <class... Args>
Node* emplace(Args&&... args) {
if (!free_nodes.empty()) {
Node* result = free_nodes.back();
free_nodes.pop_back();
std::destroy_at(result);
std::construct_at(result, std::forward<Args>(args)...);
return result;
}
if (blocks.empty() || int(blocks.back().size()) == pool_block_size) {
blocks.emplace_back();
blocks.back().reserve(pool_block_size);
}
blocks.back().emplace_back(std::forward<Args>(args)...);
return &blocks.back().back();
}
void recycle(Node* node) {
free_nodes.push_back(node);
}
};
inline static Pool pool;
Node* root;
Compare comp;
static int subtree_size(const Node* t) {
return t == nullptr ? 0 : t->size;
}
Node* new_node(T key) {
return pool.emplace(std::move(key));
}
static void update(Node* t) {
t->size = 1 + subtree_size(t->l) + subtree_size(t->r);
}
static Node* rotate_right(Node* t) {
Node* s = t->l;
t->l = s->r;
s->r = t;
update(t);
update(s);
return s;
}
static Node* rotate_left(Node* t) {
Node* s = t->r;
t->r = s->l;
s->l = t;
update(t);
update(s);
return s;
}
static Node* balance(Node* t) {
if (t == nullptr) return nullptr;
const int left_size = subtree_size(t->l);
const int right_size = subtree_size(t->r);
if (left_size + right_size > 1 && left_size > 3LL * right_size) {
if (subtree_size(t->l->r) >= 2LL * subtree_size(t->l->l)) {
t->l = rotate_left(t->l);
}
return rotate_right(t);
}
if (left_size + right_size > 1 && right_size > 3LL * left_size) {
if (subtree_size(t->r->l) >= 2LL * subtree_size(t->r->r)) {
t->r = rotate_right(t->r);
}
return rotate_left(t);
}
update(t);
return t;
}
static Node* join_with_root(Node* l, Node* middle, Node* r) {
const int left_size = subtree_size(l);
const int right_size = subtree_size(r);
if (left_size > 3LL * (right_size + 1)) {
l->r = join_with_root(l->r, middle, r);
return balance(l);
}
if (right_size > 3LL * (left_size + 1)) {
r->l = join_with_root(l, middle, r->l);
return balance(r);
}
middle->l = l;
middle->r = r;
return balance(middle);
}
static Node* detach_max(Node* t, Node*& maximum) {
if (t->r == nullptr) {
maximum = t;
return t->l;
}
t->r = detach_max(t->r, maximum);
return balance(t);
}
static Node* merge_nodes(Node* l, Node* r) {
if (l == nullptr || r == nullptr) return l == nullptr ? r : l;
Node* middle;
l = detach_max(l, middle);
return join_with_root(l, middle, r);
}
std::pair<Node*, Node*> split_nodes(Node* t, const T& key) {
if (t == nullptr) return {nullptr, nullptr};
Node* left = t->l;
Node* right = t->r;
t->l = nullptr;
t->r = nullptr;
if (comp(t->key, key)) {
auto [l, r] = split_nodes(right, key);
return {join_with_root(left, t, l), r};
}
auto [l, r] = split_nodes(left, key);
return {l, join_with_root(r, t, right)};
}
Node* insert_impl(Node* t, T& key, bool& inserted) {
if (t == nullptr) {
inserted = true;
return new_node(std::move(key));
}
if (comp(key, t->key)) {
t->l = insert_impl(t->l, key, inserted);
} else if (comp(t->key, key)) {
t->r = insert_impl(t->r, key, inserted);
} else {
return t;
}
if (!inserted) return t;
return balance(t);
}
Node* erase_impl(Node* t, const T& key, bool& erased) {
if (t == nullptr) return nullptr;
if (comp(key, t->key)) {
t->l = erase_impl(t->l, key, erased);
} else if (comp(t->key, key)) {
t->r = erase_impl(t->r, key, erased);
} else {
erased = true;
Node* l = t->l;
Node* r = t->r;
pool.recycle(t);
return merge_nodes(l, r);
}
if (!erased) return t;
return balance(t);
}
static const T* kth_impl(const Node* t, int k) {
while (t != nullptr) {
const int left_size = subtree_size(t->l);
if (k < left_size) {
t = t->l;
} else if (k == left_size) {
return &t->key;
} else {
k -= left_size + 1;
t = t->r;
}
}
return nullptr;
}
int order_of_key_impl(const Node* t, const T& key, bool upper) const {
int result = 0;
while (t != nullptr) {
const bool take = upper ? !comp(key, t->key) : comp(t->key, key);
if (take) {
result += subtree_size(t->l) + 1;
t = t->r;
} else {
t = t->l;
}
}
return result;
}
const T* lower_bound_impl(const Node* t, const T& key, bool strict) const {
const T* result = nullptr;
while (t != nullptr) {
const bool candidate = strict ? comp(key, t->key) : !comp(t->key, key);
if (candidate) {
result = &t->key;
t = t->l;
} else {
t = t->r;
}
}
return result;
}
const T* max_less_impl(const Node* t, const T& key, bool strict) const {
const T* result = nullptr;
while (t != nullptr) {
const bool candidate = strict ? comp(t->key, key) : !comp(key, t->key);
if (candidate) {
result = &t->key;
t = t->r;
} else {
t = t->l;
}
}
return result;
}
bool contains_impl(const Node* t, const T& key) const {
while (t != nullptr) {
if (comp(key, t->key)) {
t = t->l;
} else if (comp(t->key, key)) {
t = t->r;
} else {
return true;
}
}
return false;
}
static void dump_impl(const Node* t, std::vector<T>& result) {
if (t == nullptr) return;
dump_impl(t->l, result);
result.push_back(t->key);
dump_impl(t->r, result);
}
static void recycle_impl(Node* t) {
if (t == nullptr) return;
recycle_impl(t->l);
recycle_impl(t->r);
pool.recycle(t);
}
Node* clone_impl(const Node* t) {
if (t == nullptr) return nullptr;
Node* result = new_node(t->key);
result->l = clone_impl(t->l);
result->r = clone_impl(t->r);
update(result);
return result;
}
OrderedSet(Node* node, Compare compare) : root(node), comp(std::move(compare)) {}
public:
explicit OrderedSet(Compare compare)
: root(nullptr), comp(std::move(compare)) {}
OrderedSet() : OrderedSet(Compare()) {}
OrderedSet(std::initializer_list<T> init, Compare compare = Compare()) : OrderedSet(std::move(compare)) {
for (const T& x : init) insert(x);
}
template <typename Iterator>
OrderedSet(Iterator first, Iterator last, Compare compare = Compare()) : OrderedSet(std::move(compare)) {
while (first != last) insert(*first++);
}
OrderedSet(const OrderedSet& other)
: root(nullptr), comp(other.comp) {
root = clone_impl(other.root);
}
OrderedSet(OrderedSet&& other) noexcept
: root(std::exchange(other.root, nullptr)), comp(std::move(other.comp)) {}
~OrderedSet() {
recycle_impl(root);
}
OrderedSet& operator=(OrderedSet other) {
swap(other);
return *this;
}
void swap(OrderedSet& other) noexcept {
using std::swap;
swap(root, other.root);
swap(comp, other.comp);
}
int size() const { return subtree_size(root); }
int unique_size() const { return size(); }
bool empty() const { return root == nullptr; }
void clear() {
recycle_impl(root);
root = nullptr;
}
bool insert(T key) {
bool inserted = false;
root = insert_impl(root, key, inserted);
return inserted;
}
bool erase(const T& key) {
bool erased = false;
root = erase_impl(root, key, erased);
return erased;
}
bool contains(const T& key) const { return contains_impl(root, key); }
int count(const T& key) const { return contains(key) ? 1 : 0; }
const T* find_by_order(int k) const {
assert(0 <= k && k < size());
return kth_impl(root, k);
}
T kth(int k) const { return *find_by_order(k); }
int order_of_key(const T& key) const { return order_of_key_impl(root, key, false); }
int count_less(const T& key) const { return order_of_key(key); }
int count_less_equal(const T& key) const { return order_of_key_impl(root, key, true); }
int count_greater(const T& key) const { return size() - count_less_equal(key); }
int count_greater_equal(const T& key) const { return size() - count_less(key); }
const T* lower_bound(const T& key) const { return lower_bound_impl(root, key, false); }
const T* upper_bound(const T& key) const { return lower_bound_impl(root, key, true); }
const T* min_ge(const T& key) const { return lower_bound(key); }
const T* min_gt(const T& key) const { return upper_bound(key); }
const T* max_le(const T& key) const { return max_less_impl(root, key, false); }
const T* max_lt(const T& key) const { return max_less_impl(root, key, true); }
const T* min() const { return empty() ? nullptr : kth_impl(root, 0); }
const T* max() const { return empty() ? nullptr : kth_impl(root, size() - 1); }
std::pair<OrderedSet, OrderedSet> split(const T& key) && {
auto [l, r] = split_nodes(root, key);
root = nullptr;
return {OrderedSet(l, comp), OrderedSet(r, std::move(comp))};
}
OrderedSet merge(OrderedSet other) && {
assert(empty() || other.empty() || comp(*max(), *other.min()));
root = merge_nodes(root, other.root);
other.root = nullptr;
return std::move(*this);
}
std::vector<T> to_vector() const {
std::vector<T> result;
result.reserve(size());
dump_impl(root, result);
return result;
}
};
} // namespace ds
} // namespace m1une
#line 5 "ds/range_query/range_majority.hpp"
#line 7 "ds/range_query/range_majority.hpp"
#include <map>
#include <optional>
#line 11 "ds/range_query/range_majority.hpp"
namespace m1une {
namespace ds {
// Strict-majority queries and point assignments in O(log N) time.
template <class T>
struct RangeMajority {
using result_type = std::optional<T>;
private:
struct Vote {
int candidate = -1;
int balance = 0;
};
int _n;
int _tree_size;
std::vector<T> _values;
std::map<T, int> _ranks;
std::vector<int> _current;
std::vector<OrderedSet<int>> _positions;
std::vector<Vote> _tree;
int register_value(T value) {
int rank = int(_values.size());
auto [iterator, inserted] = _ranks.emplace(std::move(value), rank);
if (inserted) {
_values.push_back(iterator->first);
_positions.emplace_back();
}
return iterator->second;
}
static Vote combine(Vote first, Vote second) {
if (first.balance == 0) return second;
if (second.balance == 0) return first;
if (first.candidate == second.candidate) {
return Vote{first.candidate, first.balance + second.balance};
}
if (first.balance > second.balance) {
return Vote{first.candidate, first.balance - second.balance};
}
if (second.balance > first.balance) {
return Vote{second.candidate, second.balance - first.balance};
}
return Vote();
}
Vote range_vote(int left, int right) const {
Vote left_vote;
Vote right_vote;
left += _tree_size;
right += _tree_size;
while (left < right) {
if (left & 1) left_vote = combine(left_vote, _tree[left++]);
if (right & 1) right_vote = combine(_tree[--right], right_vote);
left >>= 1;
right >>= 1;
}
return combine(left_vote, right_vote);
}
public:
RangeMajority() : _n(0), _tree_size(1), _tree(2) {}
explicit RangeMajority(const std::vector<T>& values)
: _n(int(values.size())),
_tree_size(1),
_current(values.size()) {
_values.reserve(values.size());
_positions.reserve(values.size());
while (_tree_size < _n) _tree_size <<= 1;
_tree.assign(2 * _tree_size, Vote());
for (int index = 0; index < _n; index++) {
int rank = register_value(values[index]);
_current[index] = rank;
_positions[rank].insert(index);
_tree[_tree_size + index] = Vote{rank, 1};
}
for (int node = _tree_size - 1; node > 0; node--) {
_tree[node] = combine(_tree[node << 1], _tree[(node << 1) | 1]);
}
}
int size() const {
return _n;
}
bool empty() const {
return _n == 0;
}
// Assigns value to one position. Previously unseen values are supported.
void set(int index, T value) {
assert(0 <= index && index < _n);
int rank = register_value(std::move(value));
int previous_rank = _current[index];
if (rank == previous_rank) return;
[[maybe_unused]] bool erased =
_positions[previous_rank].erase(index);
[[maybe_unused]] bool inserted = _positions[rank].insert(index);
assert(erased && inserted);
_current[index] = rank;
int node = _tree_size + index;
_tree[node] = Vote{rank, 1};
while ((node >>= 1) != 0) {
_tree[node] = combine(_tree[node << 1], _tree[(node << 1) | 1]);
}
}
// Returns the unique value occurring more than half the time in
// [left, right), or nullopt when no such value exists.
result_type query(int left, int right) const {
assert(0 <= left && left < right && right <= _n);
Vote vote = range_vote(left, right);
if (vote.balance == 0) return std::nullopt;
const OrderedSet<int>& positions = _positions[vote.candidate];
int frequency =
positions.order_of_key(right) - positions.order_of_key(left);
if (2LL * frequency <= right - left) return std::nullopt;
return _values[vote.candidate];
}
result_type majority(int left, int right) const {
return query(left, right);
}
};
} // namespace ds
} // namespace m1une