import java.util.*; // https://en.wikipedia.org/wiki/Treap public class TreapBST { static Random random = new Random(); static class Treap { int key; long prio; Treap left; Treap right; int size; Treap(int key) { this.key = key; prio = random.nextLong(); size = 1; } void update() { size = 1 + getSize(left) + getSize(right); } } static int getSize(Treap root) { return root == null ? 0 : root.size; } static class TreapPair { Treap left; Treap right; TreapPair(Treap left, Treap right) { this.left = left; this.right = right; } } static TreapPair split(Treap root, int minRight) { if (root == null) return new TreapPair(null, null); if (root.key >= minRight) { TreapPair leftSplit = split(root.left, minRight); root.left = leftSplit.right; root.update(); leftSplit.right = root; return leftSplit; } else { TreapPair rightSplit = split(root.right, minRight); root.right = rightSplit.left; root.update(); rightSplit.left = root; return rightSplit; } } static Treap merge(Treap left, Treap right) { if (left == null) return right; if (right == null) return left; // if (random.nextInt(left.size + right.size) < left.size) { if (left.prio > right.prio) { left.right = merge(left.right, right); left.update(); return left; } else { right.left = merge(left, right.left); right.update(); return right; } } static Treap insert(Treap root, int key) { TreapPair t = split(root, key); return merge(merge(t.left, new Treap(key)), t.right); } static Treap insert2(Treap root, int key) { // alternative implementation return insert_(root, new Treap(key)); } static Treap insert_(Treap root, Treap node) { if (root == null) { return node; } if (root.prio < node.prio) { TreapPair t = split(root, node.key); node.left = t.left; node.right = t.right; node.update(); return node; } if (node.key < root.key) { root.left = insert_(root.left, node); root.update(); return root; } else { root.right = insert_(root.right, node); root.update(); return root; } } static Treap remove(Treap root, int key) { TreapPair t = split(root, key); return merge(t.left, split(t.right, key + 1).right); } static Treap remove2(Treap root, int key) { // alternative implementation if (root == null) { return null; } if (key < root.key) { root.left = remove(root.left, key); root.update(); return root; } else if (key > root.key) { root.right = remove(root.right, key); root.update(); return root; } else { return merge(root.left, root.right); } } static int kth(Treap root, int k) { if (k < getSize(root.left)) return kth(root.left, k); else if (k > getSize(root.left)) return kth(root.right, k - getSize(root.left) - 1); return root.key; } static void print(Treap root) { if (root == null) return; print(root.left); System.out.println(root.key); print(root.right); } // O(n) treap creation (if not counting keys sorting) // http://wcipeg.com/wiki/Cartesian_tree static Treap createTreap(int[] keys) { Treap[] nodes = Arrays.stream(keys).mapToObj(Treap::new).sorted((a, b) -> Integer.compare(a.key, b.key)).toArray(Treap[]::new); int n = keys.length; int[] parent = new int[n]; Arrays.fill(parent, -1); Treap root = n > 0 ? nodes[0] : null; for (int i = 1; i < n; i++) { int last = i - 1; if (nodes[last].prio >= nodes[i].prio) { nodes[last].right = nodes[i]; parent[i] = last; } else { while (nodes[last].prio < nodes[i].prio && parent[last] != -1) { last = parent[last]; } if (nodes[last].prio < nodes[i].prio) { nodes[i].left = nodes[last]; root = nodes[i]; } else { nodes[i].left = nodes[last].right; nodes[last].right = nodes[i]; parent[i] = last; } } } updateSizes(root); return root; } private static void updateSizes(Treap root) { if (root == null) return; updateSizes(root.left); updateSizes(root.right); root.update(); } // random test public static void main(String[] args) { long time = System.currentTimeMillis(); Treap treap = null; Set set = new TreeSet<>(); for (int i = 0; i < 1000_000; i++) { int key = random.nextInt(100_000); if (random.nextBoolean()) { treap = remove(treap, key); set.remove(key); } else if (!set.contains(key)) { treap = insert(treap, key); set.add(key); } if (set.size() != getSize(treap)) throw new RuntimeException(); } for (int i = 0; i < getSize(treap); i++) { if (!set.contains(kth(treap, i))) throw new RuntimeException(); } System.out.println(System.currentTimeMillis() - time); for (int step = 0; step < 1000; step++) { int n = random.nextInt(10) + 1; int[] keys = random.ints(n, 0, 100).toArray(); Treap t = createTreap(keys); Arrays.sort(keys); for (int i = 0; i < n; i++) { if (kth(t, i) != keys[i]) throw new RuntimeException(); } checkHeapInvariant(t); } } private static void checkHeapInvariant(Treap root) { if (root == null) return; if (root.left != null && root.left.prio > root.prio) throw new RuntimeException(); if (root.right != null && root.right.prio > root.prio) throw new RuntimeException(); checkHeapInvariant(root.left); checkHeapInvariant(root.right); } }