std::set can’t answer “-th smallest” or “how many are less than ”. GNU’s policy-based data structures (PBDS) can, in . They also provide a meldable heap with decrease-key and a fast hash table, and __gnu_cxx::rope gives a sequence with splits.

Order-statistics tree

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
template<class T> using oset = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
 
oset<int> s;
s.insert(x); s.erase(x);
*s.find_by_order(k);      // k-th smallest, 0-indexed (end() if k >= size)
s.order_of_key(x);        // number of elements < x

Multiset: store pair<T, int> with a unique id; order_of_key({x, INT_MIN}) counts elements below , and erase via s.erase(s.lower_bound({x, INT_MIN})). Using less_equal as the comparator “works” for inserts but breaks find, erase and lower_bound semantics; avoid it.

Map with order statistics: tree<K, V, less<K>, rb_tree_tag, tree_order_statistics_node_update>.

Custom node updates: PBDS lets you write your own node_update to maintain subtree aggregates (sums, max) in the tree. A treap is usually easier.

Faster alternatives when values are known

ApproachQueryNotes
PBDS tree, big constantonline, any comparable keys
Fenwick over compressed values, 5-10x fasterneeds all values up front
Fenwick with bitwise descent-th in one walksee Fenwick Tree
segment tree over valuessupports more aggregates (sum of smallest)
treap with sizesfully customisable, splits and merges
sqrt decomposition over valuessimplest to reason about, when isn’t needed
64-ary bit treepredecessor/successor in integers only, no -th

Typical uses

ProblemOperation
inversions onlineinsert right to left, order_of_key
sliding window median / -thinsert and erase as the window moves
Josephus elimination orderfind_by_order((pos + k - 1) % size) and erase
rank of each element when insertedorder_of_key before insert
count values in order_of_key(r + 1) - order_of_key(l)

PBDS priority queue

#include <ext/pb_ds/priority_queue.hpp>
using PQ = __gnu_pbds::priority_queue<ll, greater<ll>, __gnu_pbds::pairing_heap_tag>;   // min-heap
PQ q; auto it = q.push(5);
q.modify(it, 3);          // decrease (or increase) key through the iterator
q.erase(it);
q.join(other);            // meld in O(1); other becomes empty

Pairing heap: push and join, amortized pop/modify. It enables Dijkstra with real decrease-key (rarely faster than lazy deletion in practice) and meldable heaps for “merge the queues of two children” problems.

Rope

#include <ext/rope>
using namespace __gnu_cxx;
rope<int> r;
r.push_back(x); r.insert(pos, x); r.erase(pos, len);
rope<int> piece = r.substr(pos, len);     // shares nodes: O(log n)
r.insert(pos, piece); r[i];

Copying a rope is (persistent, shared structure), so “copy a segment and paste it somewhere else” or “keep every version” problems become one-liners. It’s 2-5x slower than a hand-written implicit treap. Memory can blow up with many versions.

Portability

PBDS and rope are libstdc++ extensions: they work with GCC on Codeforces, AtCoder and ICPC systems, but not with MSVC or libc++. Keep a treap in the library as a fallback (Treaps).

Other PBDS containers

gp_hash_table is a fast open-addressing hash map — typically 3-5× faster than unordered_map and, with a randomised hash (splitmix64, time-seeded), resistant to anti-hash tests (the default integer hash is the identity, trivially hackable). It’s the usual pairing with the order-statistics tree when a problem needs both a fast map and a fast ordered structure, and shares the same GCC-only portability caveat above.