Treap


Overview

Note:

  • Cover Advanced Topics if time available

Definition

  • tree + heap
  • tree for data
  • heap for shape
  • balanced on average if priority is assigned randomly

Binary Search Tree

  • data structure for binaray search
  • \(\mathrm{left child < parent < right child \quad \forall nodes}\)
  • insert, delete, search for element
  • \(O(\text{height})\)
  • problem : degenerate(退化)


degenerate BST

  • this tree is inserted with in the order of [5, 6, 7, 8, 9]
  • degenerate to linked list, \(O(\text{height}) = O(n)\)


Heap

  • \(\mathrm{parent > children \quad \forall nodes}\)

Treap

  • a node consists of key and priority
  • key : tree
    • \(\mathrm{key_{left\ child} < key_{parent} < key_{right\ child} \quad\forall nodes}\)
  • priority : heap
    • \(\mathrm{priority_{parent} > priority_{children} \quad\forall\ nodes}\)


Balance Property

  • Observation : root is inserted before children in BST
  • \(\mathrm{priority_{parent} > priority_{children} \quad \forall nodes}\)
  • View as a BST whose insertion order is decided by priorities(larger-> earlier)
  • why balance?
  • priority is given randomly \(\implies\) is balanced on average

Note:

  • a single proof for balance property
  • insertion is decided by piority, larger priority -> inserted earlier
  • emphasize priority decide insertion order if view tree as final tree
  • randomized BST
  • size as probability weight implementation
    • when merge, \(\Pr(a ~\text{as root}) = \frac{\text{size}(a)}{\text{size}(a)+\text{size}(b)}\)
    • more rand() calls but use less memory

Usage

  • Easy to code balanced BST
  • Merge / Split operation
  • Implicit Treap
  • Serve as powerful interval tree(線段樹)
  • Free to add additional features!
  • Implementation details will be cover later

Note:

  • interval tree? segment tree? 名稱緣由
  • Should give a sense that treap is strong
  • talk about merge/split a little
  • details should be cover in implementation

Implicit Treap

  • idea : index as key
  • blanced array
  • text-editor (rope)

Note:

  • when implement, record size instead of index
  • interval information maintain

Treap as interval tree

  • implicit treap with extra information
  • interval(subtree) min/max/sum
  • interval fold with associative law (結合律) \(G(S,*)~\text{s.t.}~\forall a, b, c\in S,\ a*b*c\ =\ (a*b)*c\ =\ a*(b*c)\)
  • lazy mark (support nterval update)
  • more!
    • delete interval
    • insert interval

Note:

  • talk about concept of merge and split
  • cover details and code later

Interval Treap

Note:

  • Each point is array value itself and record the interval information of its subtree

Implementation

  • Merge-split Treap
  • Treap Node
    • pri, key (or size), val
    • (optional) lazy mark
  • Basic Operations
    • merge, split
    • insert, delete (combine merge and split to achieve)
    • pull, push (for interval tree)

Note:(can mention a little)

  • We use merge-split treap most of the time
  • advantages:
    • easy to code (~iterval tree)
    • more powerful (merge and split supported)
  • downsides:
    • constant bigger than rotate treap

Treap Node Code

struct Node
{
  int pri, key, sz, val;
  Node *lc, *rc;
  Node(){};
  //syntax sugar
  Node(int val, int key) :pri(rand()), key(key), sz(1), val(val), mark(0) {};
  Node(int val) :pri(rand()), sz(1), val(val), mark(0) {};
}
  • hint : without srand(time(NULL)) sometimes get TLE
  • Node means subTreap

Note:

  • if no srand, rand() result is predictible by problem setter
  • and problem setter can 搞你
  • Node in merge-split Treap can be seen as subtree rooted at that Node

Basic Operations

  • merge
    • merge treap A and treap B
    • \(\text{key}_a < \text{key}_b \quad \forall a \in A,\ b \in B\)
  • split
    • split treap C to treap A and treap B by key value k
    • \(\text{key}_a \leqslant k < \text{key}_b \quad \forall a \in A,\ b \in B\)
  • inverse operations, both are \(O(\log(n))\) in expectation
  • merge + split combinations

Merge

  • priority大的當根, 剩下交給子樹處理




  • 很清楚的是\(O(\log(n))\)
  • 注意到左右永不混雜


Merge

  • merge trees rooted at a and b, return merged tree root
  • \(\text{key}_a < \text{key}_b \quad \forall a \in A, b \in B\)
  • implicit treap?
  • No Change, why?
Node* merge(Node* a, Node* b) //key a < key b
{
    if (!a) return b; //base case
    if (!b) return a;
    if(a−>pri > b−>pri)//pri a bigger => a as root
    {
        a−>r = merge(a−>r, b); //key b bigger => merge b and rc of a
        return a;
    }
    //b as root
    b−>l = merge(a, b−>l); //key a smaller => merge a and lc of b
    return b;
}

Note:

  • Merge preserve BST order, so when still work on implicit key

Split

  • split treap C to treap A and treap B by key value k
  • 如果當前的根的key \(\leq\) k
  • 根及左子樹都屬於A, 右邊再討論
  • 遞迴右邊的結果要接回根當前根的右邊






Split

  • split treap C to treap A and treap B by key value k
  • \(\text{key}_a \leqslant k < \text{key}_b \quad \forall a \in A,\ b \in B\)
  • Q : why heap property still holds
void split(Node* c, int k, Node*& a, Node*& b) 
{
  if (!c) a = b = nullptr; //base case
  else if (c−>key <= k)
  { //key of c is smaller than k =>
    a = c;
    split(c−>r, k, a−>r, b);
  }
  else
  {
    b = c;
    split(c−>l, k, a, b−>l);
  }
}

Note:

  • result will store in a, b (passed by reference)
  • Answer of Q : because all nodes' children are deeper than themself
  • No need to judge priority!!

Split - Implicit Treap

  • implicit treap?
  • record size and change k while cutting

Note:

  • try to code yourself

Insert

  • Insert(C, x)
  • Split(C, x, A, B)
  • Merge(Merge(A, x), B)
  • get AxB
  • insert interval?

Note:

  • interval(as treap) is nothing special

Delete

  • Delete(C, x)
  • Split(C, <x, A, B), Split(C, x, D, E)
  • Merge(A, E)
  • get A(no x)E
  • delete interval?delete "single x"?

Note:

  • Answer to delete a single x : D = find(C, x), D = Merge(D->lc, D->rc)

For Interval Treap

  • very similar to interval tree
  • check validity of information!
  • write push and pull function for clarity
    • push (down)
      • before use subtrees
      • 根處理好了剩下「推」給子樹處理
    • pull (up)
      • when merge subtrees
      • 子樹處理好的資訊「拉」上來

Note:

  • 叫pull up的時候資訊一定要是對的
  • 叫push dpwn的時候自己要處理好

Example (POJ 3580 Super Memo)

void add_(Node *&root, int l, int r, int x)
{
  Node *a, *b, *c, *d;
  split(root, r, a, b); //a = [1, r]
  split(a, l-1, c, d); //d = [l, r]
  d->addv += x;
  root = merge(merge(c, d), b);
}
Node *merge(Node *a, Node *b) //key a < key b
{
  if(!a) return b;
  if(!b) return a;
  if(a->pri > b->pri)
  {
    push(a);
    a->rc = merge(a->rc, b);
    pull(a);
    return a;
  }
  push(b);
  b->lc = merge(a, b->lc);
  pull(b);
  return b;
}
void push(Node *a)
{
  if(!a) return;
  if(a->rev)
  {
    swap(a->lc, a->rc);
    if(a->lc) a->lc->rev ^= 1;
    if(a->rc) a->rc->rev ^= 1;
    a->rev = false;
  }
  if(a->addv != 0)
  {
    a->val += a->addv;
    a->minv += a->addv;
    if(a->lc) a->lc->addv += a->addv;
    if(a->rc) a->rc->addv += a->addv;
    a->addv = 0;
  }
  return;
}
void pull(Node *a)
{
  a->sz = sz(a->lc) + sz(a->rc) + 1;
  a->minv = a->val;
  //key : push children before pull (maintain inside) or consider addv lazy mark
  if(a->lc) a->minv = min(a->minv, a->lc->minv + a->lc->addv);
  if(a->rc) a->minv = min(a->minv, a->rc->minv + a->rc->addv);
}

Note:

  • From my AC code
  • 21277193 maxwill 3580 Accepted 6140K 1532MS G++ 4975B 2020-01-29 13:41:32

PBDS and Rope

  • pbds BST : binary search tree
  • rope : text editor, implicit BST
  • 如果必須實作, ICPC 可以帶模板 但是要夠熟悉賽場上才容易寫得出來變化題
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
#include <ext/rope>
using namespace __gnu_cxx;

typedef tree<int,null_type,less<int>,rb_tree_tag,
tree_order_statistics_node_update> ordered_set;

Note:

  • pbds is strong, if only need basic treap op (find kth), pbds/rope is enough
  • ICPC 是可以帶模板的, 但是要夠熟悉賽場上才容易寫得出來變化題
  • quick introduction and jump to details

pbds BST example

  • support find_by_order and order_of_key
ordered_set X;
X.insert(1);
X.insert(2);
X.insert(4);
X.insert(8);
X.insert(16);

cout<<*X.find_by_order(1)<<endl; // 2
cout<<*X.find_by_order(2)<<endl; // 4
cout<<*X.find_by_order(4)<<endl; // 16
cout<<(end(X)==X.find_by_order(6))<<endl; // true

cout<<X.order_of_key(-5)<<endl;  // 0
cout<<X.order_of_key(1)<<endl;   // 0
cout<<X.order_of_key(3)<<endl;   // 2
cout<<X.order_of_key(400)<<endl; // 5

rope example

  • text-editor-like
typedef rope<char> crope; //crope = rope<char>
rope<int> rp, a, b, c;
rp[x];
rp.at(x);
rp.length();
rp.size();
rp.push_back(x);
rp.insert(pos, x);  //insert x at position pos
rp.erase(pos, x);   //erase x elements from pos
rp.replace(pos, x); //from position pos replace with x
rp.substr(pos, x);  //rp[pos:pos+x]
rp.copy(pos, x, s); //from position pos to pos+x replace with s
a = b + c; //concat
  • bulit-in Copy-on-Write
rope<int> *his[100000];
his[i] = new rope<int> (*his[i - 1]);

Advanced Topics

  • handle duplication (multi-set)
  • Copy-on-Write (可持久化)
    • history of tree (just like git)
    • concept : only copy what need to be copied
    • \(O(\log(n))\) new nodes per patch
    • example : ASK(version, kth, l, r) + Update + Online

Samples


POJ 3580 SuperMemo

  • Operations:
    • ADD x y D
    • REVERSE x y
    • REVOLVE x y T
    • INSERT x P
    • DELETE x
    • MIN x y
  • Implicit Treap -> AC
  • Good practice problem for interval operations
  • Think : how to SUM x y

Note:

  • SUM x, y => care ADD lazy mark (interval val += interval size*addv)
  • sample solution?

UVa 12538 Version Controlled IDE

  • Operatons:
    • Start with empty string
    • insert interval to position x
    • delete interval
  • Rope + Copy-on-Write = AC!
  • Challenge:
    • treap
    • what if add operation like reverse(rope and treap)

Conclusion

  • Treap is a powerful data structure in competitive programming
  • However, many problems that can be solved by Treap have alternatives
  • Get familiar with this powerful tool and strengthen your mind

Note:


Reference


Thanks All

Select a repo