競技プログラミング用 C++ ライブラリ
data_structure/partially_retroactive_priority_queue.hpp · GitHub で見る
共通ページ
#include "lib/data_structure/partially_retroactive_priority_queue.hpp"
PartiallyRetroactivePriorityQueue<long long> queue;
queue.insert(10, 5);
queue.insert(20, 2);
queue.insert_pop(30);
auto now = queue.top();
queue.set(10, 1);
queue.erase(30);
現在の要素集合の総和などは、変更通知を使って外部で管理できます。
long long sum = 0;
auto changed = [&](std::int64_t, const long long &value, bool added) {
sum += added ? value : -value;
};
PartiallyRetroactivePriorityQueue<long long> queue;
queue.insert(10, 5, changed); // sum = 5
queue.insert(20, 2, changed); // sum = 7
queue.insert_pop(30, changed); // sum = 5
queue.set(10, 1, changed); // sum = 2
queue.erase(30, changed); // sum = 3
PartiallyRetroactivePriorityQueue<T, Compare = std::less<T>, Time = std::int64_t> は、過去の操作を変更し、現在の状態をクエリる優先度付きキューです。std::less<T> では最小値、std::greater<T> では最大値を先頭にします。同順位の要素は挿入時刻順です。時刻は64 bit以下の整数で、各時刻に置ける操作は1つです。
| 操作 | 内容・返り値 | 計算量 |
|---|---|---|
PartiallyRetroactivePriorityQueue(compare = {},seed = 0) |
空の履歴を構築 | \(O(1)\) |
PartiallyRetroactivePriorityQueue(updates,compare = {},seed = 0) |
有効な操作履歴から構築 | \(O(m\log m)\) |
insert(t,x) |
時刻 t に push(x) を追加 |
償却期待 \(O(\log m)\) |
insert_pop(t) |
時刻 t に先頭要素の削除を追加 |
償却期待 \(O(\log m)\) |
set(t,x) |
既存の push の値を変更 |
償却期待 \(O(\log m)\) |
erase(t) |
時刻 t の操作を取り消す |
償却期待 \(O(\log m)\) |
insert(t,x,f) / insert_pop(t,f)
|
操作を追加し、現在の集合の変化を f に通知 |
償却期待 \(O(\log m)\) |
set(t,x,f) / erase(t,f)
|
操作を変更・取消し、現在の集合の変化を f に通知 |
償却期待 \(O(\log m)\) |
top() |
現在の先頭を optional<T> で取得。空なら nullopt
|
\(O(1)\) |
size() / empty()
|
現在の要素数/空判定 | \(O(1)\) |
contains_time(t) / operation_count()
|
時刻 t の操作の有無/操作数 |
contains_time: 期待 \(O(\log m)\)、operation_count: \(O(1)\) |
assign(updates) |
操作履歴をまとめて設定。無効なら false を返して元の履歴を保つ | \(O(M+m\log m)\) |
assign(updates,f) |
履歴を設定し、旧集合の削除・新集合の追加を通知。無効なら通知しない | \(O(M+m\log m)\) |
reserve(m) / clear()
|
領域予約/容量を残して履歴を初期化 | \(O(M)\) |
clear(f) |
現在の全要素の削除を通知して初期化 | \(O(M)\) |
for_each(f) |
現在の全要素について f(挿入時刻,const T&) を挿入時刻順に呼ぶ |
\(O(m)\) |
memory_usage() |
確保済み領域のバイト数 | \(O(1)\) |
swap(other) |
内容・比較関数・乱数生成器の状態を交換 | \(O(1)\) |
top_ptr() |
現在の先頭の const T*。空なら nullptr。次の変更操作まで有効 |
\(O(1)\) |
履歴変更は成功時に true を返します。時刻の衝突、対象操作の不在、set の対象が pop の場合や、空キューへの削除が発生する変更は false を返し、履歴を保ちます。
変更通知は f(挿入時刻,const T&,bool added) です。added=true は現在の集合への追加、false は現在の集合からの削除です。指定した操作の値ではなく、過去の変更の結果として現在の集合で増減する要素を通知します。同じ値も挿入時刻で区別でき、総和・XOR・頻度・外部の集合などを管理できます。コールバックは呼び出しごとに渡し、キューには保存しません。コピーできないコールバックにも対応します。
insert・insert_pop・erase は成功時に 1 要素、set は削除・追加を各高々 1 要素通知します。set は削除を先に通知し、内部の一時的な追加と削除は通知しません。変更を拒否した場合は通知しません。assign は同じ要素でも旧集合から一度削除して新集合へ追加します。既存のキューから集計を始める場合は for_each で初期化できます。継続して集計する場合は、全ての変更に同じ通知処理を渡してください。swap・コピー・ムーブ・通知なしの変更は通知しません。
通知中の参照はその呼び出し中だけ有効です。コールバックから同じキューを参照・変更したり、例外を投げたりしないでください。
updates は std::vector<Queue::update_type> で、要素は {時刻, std::optional<T>}、nullopt は削除操作です。順不同で渡せます。Queue(updates, compare = Compare{}, seed = 0) による構築には有効な履歴を渡します。空列の構築は Queue(compare = Compare{}, seed = 0) です。top_ptr() の戻り値は次の変更操作まで使用できます。削除ノードは再利用し、clear() は確保容量を残します。
操作数を \(m\)、過去の最大操作数を \(M\) とします。履歴変更は償却期待 \(O(\log m)\)、contains_time は期待 \(O(\log m)\)、現在のクエリ・operation_count・memory_usage は \(O(1)\)、構築は \(O(m\log m)\)、assign は \(O(M+m\log m)\)、reserve・clear は \(O(M)\)、for_each は \(O(m)\) です。変更通知付きも通知処理が \(O(1)\) なら同じ計算量です。空間は \(O(M)\)(予約容量が大きい場合はその容量分)です。比較・値のコピーを \(O(1)\) とします。
#ifndef PARTIALLY_RETROACTIVE_PRIORITY_QUEUE_HPP
#define PARTIALLY_RETROACTIVE_PRIORITY_QUEUE_HPP
#include "../components/splitmix64.hpp"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
template <class T, class Compare = std::less<T>, class Time = std::int64_t>
class PartiallyRetroactivePriorityQueue {
static_assert(std::is_integral_v<Time> && !std::is_same_v<Time, bool> && sizeof(Time) <= 8);
struct Ignore { void operator()(Time, const T &, bool) const {} };
struct Node {
Time time{};
std::optional<T> value;
int left = 0, right = 0, size = 0, sum = 0, low = 0, count = 0, minimum_count = 0, alive = 0, dead = 0;
std::uint32_t priority = 0;
int state = 0;
};
std::vector<Node> nodes{1};
[[no_unique_address]] Compare compare;
SplitMix64 rng;
int root = 0, free = 0;
bool less(int a, int b) const {
if (compare(*nodes[a].value, *nodes[b].value)) return true;
if (compare(*nodes[b].value, *nodes[a].value)) return false;
return nodes[a].time < nodes[b].time;
}
int smaller(int a, int b) const { return !a ? b : !b || less(a, b) ? a : b; }
int larger(int a, int b) const { return !a ? b : !b || less(b, a) ? a : b; }
void pull(int x) {
auto &n = nodes[x];
const auto &a = nodes[n.left], &b = nodes[n.right];
int c = n.value ? 1 : -1;
n.size = a.size + 1 + b.size;
n.sum = a.sum + n.state + b.sum;
n.low = std::min(a.low, a.sum + n.state + b.low);
n.count = a.count + c + b.count;
n.minimum_count = std::min(a.minimum_count, a.count + c + b.minimum_count);
n.alive = smaller(smaller(a.alive, n.value && n.state == 0 ? x : 0), b.alive);
n.dead = larger(larger(a.dead, n.state == 1 ? x : 0), b.dead);
}
int make(Time time, std::optional<T> value) {
int x;
if (free) { x = free; free = nodes[x].left; nodes[x] = Node{}; }
else {
assert(nodes.size() < std::size_t(std::numeric_limits<int>::max()));
x = nodes.size(); nodes.emplace_back();
}
nodes[x].time = time;
nodes[x].value = std::move(value);
nodes[x].priority = std::uint32_t(rng());
nodes[x].state = nodes[x].value ? 0 : -1;
pull(x);
return x;
}
int merge(int a, int b) {
if (!a || !b) return a | b;
if (nodes[a].priority > nodes[b].priority) {
nodes[a].right = merge(nodes[a].right, b); pull(a); return a;
}
nodes[b].left = merge(a, nodes[b].left); pull(b); return b;
}
std::pair<int, int> split(int x, int k) {
if (!x) return {0, 0};
int n = nodes[nodes[x].left].size;
if (k <= n) {
auto [a, b] = split(nodes[x].left, k);
nodes[x].left = b; pull(x); return {a, x};
}
auto [a, b] = split(nodes[x].right, k - n - 1);
nodes[x].right = a; pull(x); return {x, b};
}
std::pair<int, int> locate(Time time) const {
int x = root, rank = 0;
while (x) {
if (time < nodes[x].time) x = nodes[x].left;
else {
int n = nodes[nodes[x].left].size;
if (time == nodes[x].time) return {rank + n, x};
rank += n + 1; x = nodes[x].right;
}
}
return {rank, 0};
}
int bridge_dead(int x, int offset) const {
if (!x) return 0;
int a = nodes[x].left, b = nodes[x].right;
int after = offset + nodes[a].sum + nodes[x].state;
if (after + nodes[b].low == 0) return bridge_dead(b, after);
return larger(larger(bridge_dead(a, offset), nodes[x].state == 1 ? x : 0), nodes[b].dead);
}
int bridge_alive(int x, int offset) const {
if (offset == 0) return 0;
assert(x);
int a = nodes[x].left;
if (offset + nodes[a].low == 0) return bridge_alive(a, offset);
int after = offset + nodes[a].sum + nodes[x].state;
return smaller(smaller(nodes[a].alive, nodes[x].value && nodes[x].state == 0 ? x : 0), bridge_alive(nodes[x].right, after));
}
void change(int x, int key, int state) {
assert(x);
if (x == key) nodes[x].state = state;
else change(nodes[key].time < nodes[x].time ? nodes[x].left : nodes[x].right, key, state);
pull(x);
}
struct Selection { int tree, key; };
Selection promote(int a, int b) const {
int key = bridge_dead(a, 0);
return larger(key, nodes[b].dead) == key ? Selection{a, key} : Selection{b, nodes[b].dead};
}
Selection demote(int a, int b, int offset) const {
int key = bridge_alive(b, offset);
return smaller(nodes[a].alive, key) == key ? Selection{b, key} : Selection{a, nodes[a].alive};
}
void change(Selection selected, int state) { change(selected.tree, selected.key, state); }
int insert_push(int a, int b, int x) {
auto selected = promote(a, b);
int added = x;
if (selected.key && less(x, selected.key)) {
change(selected, 0); nodes[x].state = 1; pull(x);
added = selected.key;
}
root = merge(merge(a, x), b);
return added;
}
template <class F> bool erase_at(int rank, F &&on_change) {
auto [a, tail] = split(root, rank);
auto [x, b] = split(tail, 1);
if (nodes[x].value && nodes[a].count + nodes[b].minimum_count < 0) {
root = merge(merge(a, x), b); return false;
}
int changed = x;
bool added = nodes[x].state == -1;
if (added) {
auto selected = promote(a, b);
changed = selected.key; change(selected, 0);
} else if (nodes[x].state == 1) {
auto selected = demote(a, b, nodes[a].sum + 1);
changed = selected.key; change(selected, 1);
}
root = merge(a, b);
on_change(changed, added);
nodes[x].value.reset(); nodes[x].left = free; free = x;
return true;
}
template <class F> void visit(int x, F &f) const {
if (!nodes[x].alive) return;
visit(nodes[x].left, f);
if (nodes[x].value && nodes[x].state == 0) f(nodes[x].time, *nodes[x].value);
visit(nodes[x].right, f);
}
template <class F> void notify(int x, bool added, F &f) const { f(nodes[x].time, *nodes[x].value, added); }
public:
using update_type = std::pair<Time, std::optional<T>>;
explicit PartiallyRetroactivePriorityQueue(Compare compare = Compare{}, std::uint64_t seed = 0) : compare(std::move(compare)), rng(seed) {}
PartiallyRetroactivePriorityQueue(const std::vector<update_type> &updates, Compare compare = Compare{}, std::uint64_t seed = 0)
: PartiallyRetroactivePriorityQueue(std::move(compare), seed) {
[[maybe_unused]] bool ok = assign(updates);
assert(ok);
}
PartiallyRetroactivePriorityQueue(const PartiallyRetroactivePriorityQueue &) = default;
PartiallyRetroactivePriorityQueue &operator=(const PartiallyRetroactivePriorityQueue &) = default;
PartiallyRetroactivePriorityQueue(PartiallyRetroactivePriorityQueue &&other) : PartiallyRetroactivePriorityQueue(other.compare) { swap(other); }
PartiallyRetroactivePriorityQueue &operator=(PartiallyRetroactivePriorityQueue &&other) {
if (this != &other) { PartiallyRetroactivePriorityQueue next(std::move(other)); swap(next); }
return *this;
}
void swap(PartiallyRetroactivePriorityQueue &other) {
using std::swap;
nodes.swap(other.nodes); swap(compare, other.compare); swap(rng, other.rng); swap(root, other.root); swap(free, other.free);
}
bool insert(Time time, T value) { return insert(time, std::move(value), Ignore{}); }
template <class F> bool insert(Time time, T value, F &&on_change) {
auto [rank, found] = locate(time);
if (found) return false;
int x = make(time, std::move(value));
auto [a, b] = split(root, rank);
int added = insert_push(a, b, x);
notify(added, true, on_change); return true;
}
bool insert_pop(Time time) { return insert_pop(time, Ignore{}); }
template <class F> bool insert_pop(Time time, F &&on_change) {
auto [rank, found] = locate(time);
if (found) return false;
auto [a, b] = split(root, rank);
if (nodes[a].count + nodes[b].minimum_count < 1) { root = merge(a, b); return false; }
auto selected = demote(a, b, nodes[a].sum);
change(selected, 1);
int x = make(time, std::nullopt);
root = merge(merge(a, x), b);
notify(selected.key, false, on_change); return true;
}
bool set(Time time, T value) { return set(time, std::move(value), Ignore{}); }
template <class F> bool set(Time time, T value, F &&on_change) {
auto [rank, found] = locate(time);
if (!found || !nodes[found].value) return false;
int x = make(time, std::move(value));
auto [a, b] = split(root, rank);
int added = insert_push(a, b, x);
bool canceled = false;
[[maybe_unused]] bool ok = erase_at(rank + 1, [&](int key, bool inserted) {
assert(!inserted); (void)inserted;
if (key == added) canceled = true;
else notify(key, false, on_change);
});
assert(ok);
if (!canceled) notify(added, true, on_change);
return true;
}
bool erase(Time time) { return erase(time, Ignore{}); }
template <class F> bool erase(Time time, F &&on_change) {
auto [rank, found] = locate(time);
return found && erase_at(rank, [&](int key, bool added) { notify(key, added, on_change); });
}
bool contains_time(Time time) const { return locate(time).second != 0; }
const T *top_ptr() const { int x = nodes[root].alive; return x ? &*nodes[x].value : nullptr; }
std::optional<T> top() const { auto p = top_ptr(); return p ? std::optional<T>(*p) : std::nullopt; }
int size() const { return nodes[root].count; }
bool empty() const { return size() == 0; }
int operation_count() const { return nodes[root].size; }
void reserve(std::size_t capacity) { nodes.reserve(capacity + 1); }
void clear() { nodes.resize(1); root = free = 0; }
template <class F> void clear(F &&on_change) {
for_each([&](Time time, const T &value) { on_change(time, value, false); });
clear();
}
template <class F> void for_each(F &&f) const { visit(root, f); }
std::size_t memory_usage() const { return nodes.capacity() * sizeof(Node); }
bool assign(const std::vector<update_type> &updates) { return assign(updates, Ignore{}); }
template <class F> bool assign(const std::vector<update_type> &updates, F &&on_change) {
auto sorted = updates;
std::sort(sorted.begin(), sorted.end(), [](const auto &a, const auto &b) { return a.first < b.first; });
int count = 0;
for (std::size_t i = 0; i < sorted.size(); ++i) {
if (i && sorted[i - 1].first == sorted[i].first) return false;
count += sorted[i].second ? 1 : -1;
if (count < 0) return false;
}
PartiallyRetroactivePriorityQueue next(compare, rng());
next.reserve(sorted.size());
std::vector<int> heap, stack;
heap.reserve(count); stack.reserve(sorted.size());
auto order = [&](int a, int b) { return next.less(b, a); };
for (auto &[time, value] : sorted) {
int x = next.make(time, std::move(value));
if (next.nodes[x].value) {
heap.push_back(x); std::push_heap(heap.begin(), heap.end(), order);
} else {
std::pop_heap(heap.begin(), heap.end(), order);
next.nodes[heap.back()].state = 1; heap.pop_back();
}
}
for (int x = 1; x < int(next.nodes.size()); ++x) {
int left = 0;
while (!stack.empty() && next.nodes[stack.back()].priority < next.nodes[x].priority) {
left = stack.back(); stack.pop_back(); next.pull(left);
}
next.nodes[x].left = left;
if (!stack.empty()) next.nodes[stack.back()].right = x;
stack.push_back(x);
}
while (!stack.empty()) { next.root = stack.back(); next.pull(next.root); stack.pop_back(); }
if constexpr (!std::is_same_v<std::remove_cvref_t<F>, Ignore>) {
for_each([&](Time time, const T &value) { on_change(time, value, false); });
next.for_each([&](Time time, const T &value) { on_change(time, value, true); });
}
swap(next); return true;
}
};
#endif
#line 1 "data_structure/partially_retroactive_priority_queue.hpp"
#line 1 "components/splitmix64.hpp"
#include <cstdint>
inline std::uint64_t splitmix64(std::uint64_t x) {
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;
x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL;
return x ^ (x >> 31);
}
class SplitMix64 {
std::uint64_t state;
public:
explicit SplitMix64(std::uint64_t seed = 0) : state(seed) {}
std::uint64_t operator()() { return splitmix64(state += 0x9e3779b97f4a7c15ULL); }
};
#line 5 "data_structure/partially_retroactive_priority_queue.hpp"
#include <algorithm>
#include <cassert>
#include <cstddef>
#line 9 "data_structure/partially_retroactive_priority_queue.hpp"
#include <functional>
#include <limits>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
template <class T, class Compare = std::less<T>, class Time = std::int64_t>
class PartiallyRetroactivePriorityQueue {
static_assert(std::is_integral_v<Time> && !std::is_same_v<Time, bool> && sizeof(Time) <= 8);
struct Ignore { void operator()(Time, const T &, bool) const {} };
struct Node {
Time time{};
std::optional<T> value;
int left = 0, right = 0, size = 0, sum = 0, low = 0, count = 0, minimum_count = 0, alive = 0, dead = 0;
std::uint32_t priority = 0;
int state = 0;
};
std::vector<Node> nodes{1};
[[no_unique_address]] Compare compare;
SplitMix64 rng;
int root = 0, free = 0;
bool less(int a, int b) const {
if (compare(*nodes[a].value, *nodes[b].value)) return true;
if (compare(*nodes[b].value, *nodes[a].value)) return false;
return nodes[a].time < nodes[b].time;
}
int smaller(int a, int b) const { return !a ? b : !b || less(a, b) ? a : b; }
int larger(int a, int b) const { return !a ? b : !b || less(b, a) ? a : b; }
void pull(int x) {
auto &n = nodes[x];
const auto &a = nodes[n.left], &b = nodes[n.right];
int c = n.value ? 1 : -1;
n.size = a.size + 1 + b.size;
n.sum = a.sum + n.state + b.sum;
n.low = std::min(a.low, a.sum + n.state + b.low);
n.count = a.count + c + b.count;
n.minimum_count = std::min(a.minimum_count, a.count + c + b.minimum_count);
n.alive = smaller(smaller(a.alive, n.value && n.state == 0 ? x : 0), b.alive);
n.dead = larger(larger(a.dead, n.state == 1 ? x : 0), b.dead);
}
int make(Time time, std::optional<T> value) {
int x;
if (free) { x = free; free = nodes[x].left; nodes[x] = Node{}; }
else {
assert(nodes.size() < std::size_t(std::numeric_limits<int>::max()));
x = nodes.size(); nodes.emplace_back();
}
nodes[x].time = time;
nodes[x].value = std::move(value);
nodes[x].priority = std::uint32_t(rng());
nodes[x].state = nodes[x].value ? 0 : -1;
pull(x);
return x;
}
int merge(int a, int b) {
if (!a || !b) return a | b;
if (nodes[a].priority > nodes[b].priority) {
nodes[a].right = merge(nodes[a].right, b); pull(a); return a;
}
nodes[b].left = merge(a, nodes[b].left); pull(b); return b;
}
std::pair<int, int> split(int x, int k) {
if (!x) return {0, 0};
int n = nodes[nodes[x].left].size;
if (k <= n) {
auto [a, b] = split(nodes[x].left, k);
nodes[x].left = b; pull(x); return {a, x};
}
auto [a, b] = split(nodes[x].right, k - n - 1);
nodes[x].right = a; pull(x); return {x, b};
}
std::pair<int, int> locate(Time time) const {
int x = root, rank = 0;
while (x) {
if (time < nodes[x].time) x = nodes[x].left;
else {
int n = nodes[nodes[x].left].size;
if (time == nodes[x].time) return {rank + n, x};
rank += n + 1; x = nodes[x].right;
}
}
return {rank, 0};
}
int bridge_dead(int x, int offset) const {
if (!x) return 0;
int a = nodes[x].left, b = nodes[x].right;
int after = offset + nodes[a].sum + nodes[x].state;
if (after + nodes[b].low == 0) return bridge_dead(b, after);
return larger(larger(bridge_dead(a, offset), nodes[x].state == 1 ? x : 0), nodes[b].dead);
}
int bridge_alive(int x, int offset) const {
if (offset == 0) return 0;
assert(x);
int a = nodes[x].left;
if (offset + nodes[a].low == 0) return bridge_alive(a, offset);
int after = offset + nodes[a].sum + nodes[x].state;
return smaller(smaller(nodes[a].alive, nodes[x].value && nodes[x].state == 0 ? x : 0), bridge_alive(nodes[x].right, after));
}
void change(int x, int key, int state) {
assert(x);
if (x == key) nodes[x].state = state;
else change(nodes[key].time < nodes[x].time ? nodes[x].left : nodes[x].right, key, state);
pull(x);
}
struct Selection { int tree, key; };
Selection promote(int a, int b) const {
int key = bridge_dead(a, 0);
return larger(key, nodes[b].dead) == key ? Selection{a, key} : Selection{b, nodes[b].dead};
}
Selection demote(int a, int b, int offset) const {
int key = bridge_alive(b, offset);
return smaller(nodes[a].alive, key) == key ? Selection{b, key} : Selection{a, nodes[a].alive};
}
void change(Selection selected, int state) { change(selected.tree, selected.key, state); }
int insert_push(int a, int b, int x) {
auto selected = promote(a, b);
int added = x;
if (selected.key && less(x, selected.key)) {
change(selected, 0); nodes[x].state = 1; pull(x);
added = selected.key;
}
root = merge(merge(a, x), b);
return added;
}
template <class F> bool erase_at(int rank, F &&on_change) {
auto [a, tail] = split(root, rank);
auto [x, b] = split(tail, 1);
if (nodes[x].value && nodes[a].count + nodes[b].minimum_count < 0) {
root = merge(merge(a, x), b); return false;
}
int changed = x;
bool added = nodes[x].state == -1;
if (added) {
auto selected = promote(a, b);
changed = selected.key; change(selected, 0);
} else if (nodes[x].state == 1) {
auto selected = demote(a, b, nodes[a].sum + 1);
changed = selected.key; change(selected, 1);
}
root = merge(a, b);
on_change(changed, added);
nodes[x].value.reset(); nodes[x].left = free; free = x;
return true;
}
template <class F> void visit(int x, F &f) const {
if (!nodes[x].alive) return;
visit(nodes[x].left, f);
if (nodes[x].value && nodes[x].state == 0) f(nodes[x].time, *nodes[x].value);
visit(nodes[x].right, f);
}
template <class F> void notify(int x, bool added, F &f) const { f(nodes[x].time, *nodes[x].value, added); }
public:
using update_type = std::pair<Time, std::optional<T>>;
explicit PartiallyRetroactivePriorityQueue(Compare compare = Compare{}, std::uint64_t seed = 0) : compare(std::move(compare)), rng(seed) {}
PartiallyRetroactivePriorityQueue(const std::vector<update_type> &updates, Compare compare = Compare{}, std::uint64_t seed = 0)
: PartiallyRetroactivePriorityQueue(std::move(compare), seed) {
[[maybe_unused]] bool ok = assign(updates);
assert(ok);
}
PartiallyRetroactivePriorityQueue(const PartiallyRetroactivePriorityQueue &) = default;
PartiallyRetroactivePriorityQueue &operator=(const PartiallyRetroactivePriorityQueue &) = default;
PartiallyRetroactivePriorityQueue(PartiallyRetroactivePriorityQueue &&other) : PartiallyRetroactivePriorityQueue(other.compare) { swap(other); }
PartiallyRetroactivePriorityQueue &operator=(PartiallyRetroactivePriorityQueue &&other) {
if (this != &other) { PartiallyRetroactivePriorityQueue next(std::move(other)); swap(next); }
return *this;
}
void swap(PartiallyRetroactivePriorityQueue &other) {
using std::swap;
nodes.swap(other.nodes); swap(compare, other.compare); swap(rng, other.rng); swap(root, other.root); swap(free, other.free);
}
bool insert(Time time, T value) { return insert(time, std::move(value), Ignore{}); }
template <class F> bool insert(Time time, T value, F &&on_change) {
auto [rank, found] = locate(time);
if (found) return false;
int x = make(time, std::move(value));
auto [a, b] = split(root, rank);
int added = insert_push(a, b, x);
notify(added, true, on_change); return true;
}
bool insert_pop(Time time) { return insert_pop(time, Ignore{}); }
template <class F> bool insert_pop(Time time, F &&on_change) {
auto [rank, found] = locate(time);
if (found) return false;
auto [a, b] = split(root, rank);
if (nodes[a].count + nodes[b].minimum_count < 1) { root = merge(a, b); return false; }
auto selected = demote(a, b, nodes[a].sum);
change(selected, 1);
int x = make(time, std::nullopt);
root = merge(merge(a, x), b);
notify(selected.key, false, on_change); return true;
}
bool set(Time time, T value) { return set(time, std::move(value), Ignore{}); }
template <class F> bool set(Time time, T value, F &&on_change) {
auto [rank, found] = locate(time);
if (!found || !nodes[found].value) return false;
int x = make(time, std::move(value));
auto [a, b] = split(root, rank);
int added = insert_push(a, b, x);
bool canceled = false;
[[maybe_unused]] bool ok = erase_at(rank + 1, [&](int key, bool inserted) {
assert(!inserted); (void)inserted;
if (key == added) canceled = true;
else notify(key, false, on_change);
});
assert(ok);
if (!canceled) notify(added, true, on_change);
return true;
}
bool erase(Time time) { return erase(time, Ignore{}); }
template <class F> bool erase(Time time, F &&on_change) {
auto [rank, found] = locate(time);
return found && erase_at(rank, [&](int key, bool added) { notify(key, added, on_change); });
}
bool contains_time(Time time) const { return locate(time).second != 0; }
const T *top_ptr() const { int x = nodes[root].alive; return x ? &*nodes[x].value : nullptr; }
std::optional<T> top() const { auto p = top_ptr(); return p ? std::optional<T>(*p) : std::nullopt; }
int size() const { return nodes[root].count; }
bool empty() const { return size() == 0; }
int operation_count() const { return nodes[root].size; }
void reserve(std::size_t capacity) { nodes.reserve(capacity + 1); }
void clear() { nodes.resize(1); root = free = 0; }
template <class F> void clear(F &&on_change) {
for_each([&](Time time, const T &value) { on_change(time, value, false); });
clear();
}
template <class F> void for_each(F &&f) const { visit(root, f); }
std::size_t memory_usage() const { return nodes.capacity() * sizeof(Node); }
bool assign(const std::vector<update_type> &updates) { return assign(updates, Ignore{}); }
template <class F> bool assign(const std::vector<update_type> &updates, F &&on_change) {
auto sorted = updates;
std::sort(sorted.begin(), sorted.end(), [](const auto &a, const auto &b) { return a.first < b.first; });
int count = 0;
for (std::size_t i = 0; i < sorted.size(); ++i) {
if (i && sorted[i - 1].first == sorted[i].first) return false;
count += sorted[i].second ? 1 : -1;
if (count < 0) return false;
}
PartiallyRetroactivePriorityQueue next(compare, rng());
next.reserve(sorted.size());
std::vector<int> heap, stack;
heap.reserve(count); stack.reserve(sorted.size());
auto order = [&](int a, int b) { return next.less(b, a); };
for (auto &[time, value] : sorted) {
int x = next.make(time, std::move(value));
if (next.nodes[x].value) {
heap.push_back(x); std::push_heap(heap.begin(), heap.end(), order);
} else {
std::pop_heap(heap.begin(), heap.end(), order);
next.nodes[heap.back()].state = 1; heap.pop_back();
}
}
for (int x = 1; x < int(next.nodes.size()); ++x) {
int left = 0;
while (!stack.empty() && next.nodes[stack.back()].priority < next.nodes[x].priority) {
left = stack.back(); stack.pop_back(); next.pull(left);
}
next.nodes[x].left = left;
if (!stack.empty()) next.nodes[stack.back()].right = x;
stack.push_back(x);
}
while (!stack.empty()) { next.root = stack.back(); next.pull(next.root); stack.pop_back(); }
if constexpr (!std::is_same_v<std::remove_cvref_t<F>, Ignore>) {
for_each([&](Time time, const T &value) { on_change(time, value, false); });
next.for_each([&](Time time, const T &value) { on_change(time, value, true); });
}
swap(next); return true;
}
};