Algorithm Search⌘K

Proofs: Persistent segment tree

These are the human proofs of Persistent segment tree, at full length.

The persistent segment tree keeps every version and answers sums and ranks correctly full

This proof shows: correctness.

Fix n≥1n \ge 1. For a node tt and a range [lo,hi)[lo, hi) with hi−lo≥1hi - lo \ge 1, say that tt is well formed for [lo,hi)[lo, hi) if either hi−lo=1hi - lo = 1 and tt is a leaf, or hi−lo≥2hi - lo \ge 2, tt is an inner node, t.leftt.left is well formed for [lo,mid)[lo, mid), t.rightt.right is well formed for [mid,hi)[mid, hi), and t.sum=t.left.sum+t.right.sumt.sum = t.left.sum + t.right.sum, where mid=⌊(lo+hi)/2⌋mid = \lfloor (lo + hi)/2 \rfloor. For such a tt, define the array val(t,lo,hi)\text{val}(t, lo, hi) by val(t,lo,lo+1)=⟨t.sum⟩\text{val}(t, lo, lo + 1) = \langle t.sum \rangle for a leaf, and val(t,lo,hi)=val(t.left,lo,mid)⋅val(t.right,mid,hi)\text{val}(t, lo, hi) = \text{val}(t.left, lo, mid) \cdot \text{val}(t.right, mid, hi) otherwise. Since lo<mid<hilo < mid < hi when hi−lo≥2hi - lo \ge 2, both ranges are shorter, so the definitions are by induction on hi−lohi - lo.

Lemma 1. If tt is well formed for [lo,hi)[lo, hi), then t.sumt.sum is the sum of val(t,lo,hi)\text{val}(t, lo, hi).

Proof. By induction on hi−lohi - lo. For a leaf both sides are t.sumt.sum. For an inner node, t.sum=t.left.sum+t.right.sumt.sum = t.left.sum + t.right.sum, and by induction these are the sums of the two halves of val(t,lo,hi)\text{val}(t, lo, hi). □\square

Lemma 2 (no change). No procedure writes a field of an existing node. Hence, if tt is well formed for [lo,hi)[lo, hi) at some moment, it stays well formed for [lo,hi)[lo, hi) and val(t,lo,hi)\text{val}(t, lo, hi) never changes.

Proof. BUILD and UPDATE only call NEW-NODE, which writes the fields of a node that did not exist before, and QUERY, GET and KTH only read. Well formedness and val\text{val} depend only on the fields of the nodes reachable from tt, and none of them changes. □\square

Lemma 3 (build). BUILD(a,lo,hi)(a, lo, hi) returns a node that is well formed for [lo,hi)[lo, hi) with val=a[lo..hi)\text{val} = a[lo..hi).

Proof. By induction on hi−lohi - lo. Line L1 handles the leaf. Otherwise lines L3 build well formed trees for the two halves with the right arrays, and line L4 makes a node whose sum is the sum of the two child sums. □\square

Lemma 4 (update). If tt is well formed for [lo,hi)[lo, hi) and lo≤i<hilo \le i < hi, then UPDATE(t,lo,hi,i,x)(t, lo, hi, i, x) returns a node t′t' that is well formed for [lo,hi)[lo, hi), with val(t′,lo,hi)\text{val}(t', lo, hi) equal to val(t,lo,hi)\text{val}(t, lo, hi) except that position ii holds xx. It makes exactly one new node per level, that is, d+1d + 1 nodes where dd is the depth of the leaf of ii.

Proof. By induction on hi−lohi - lo. If hi−lo=1hi - lo = 1 then i=loi = lo, and line L5 returns a leaf with sum xx. Otherwise let i<midi < mid (the case i≥midi \ge mid is the same with the sides swapped). By induction, ll is well formed for [lo,mid)[lo, mid) with the array of t.leftt.left changed at ii. The node r=t.rightr = t.right is well formed for [mid,hi)[mid, hi) because tt is, and its array is the old one, which has no position ii. Line L9 makes a node with these children and with the sum of their sums, so it is well formed, and its array is the concatenation, which is the old array with position ii changed. One node is made at this level plus, by induction, one per lower level on the path. □\square

Lemma 5 (query). If tt is well formed for [lo,hi)[lo, hi), then QUERY(t,lo,hi,l,r)(t, lo, hi, l, r) returns the sum of val(t,lo,hi)\text{val}(t, lo, hi) over the positions in [lo,hi)∩[l,r)[lo, hi) \cap [l, r).

Proof. By induction on hi−lohi - lo. If the ranges do not meet (line L10) the sum is empty, 0. If [lo,hi)⊆[l,r)[lo, hi) \subseteq [l, r) (line L11) the answer is the full sum, which is t.sumt.sum by Lemma 1. Otherwise hi−lo≥2hi - lo \ge 2, because a range of length 1 either is inside [l,r)[l, r) or does not meet it. Then [lo,hi)∩[l,r)[lo, hi) \cap [l, r) is the disjoint union of its parts in [lo,mid)[lo, mid) and in [mid,hi)[mid, hi), and line L13 adds the two answers, which are right by induction. □\square

Theorem 1. After any sequence of operations, for each version vv the root roots[v]roots[v] is well formed for [0,n)[0, n), and val(roots[v],0,n)\text{val}(roots[v], 0, n) is the array that the abstract type gives to version vv. Each query returns the abstract answer on its version.

Proof. By induction on the number of operations. Version 0 is right by Lemma 3. An operation update(v, i, x) adds a root that is right by Lemma 4 applied to roots[v]roots[v], which is well formed by induction. By Lemma 2 no earlier root changes its array. add is get followed by update. get follows the one path to the leaf of ii, and by the definition of val\text{val} that leaf holds position ii. query is right by Lemma 5 with [lo,hi)=[0,n)[lo, hi) = [0, n). □\square

Theorem 2 (kth). Let uu and vv be roots, both well formed for [0,n)[0, n), with D=val(v)−val(u)≥0D = \text{val}(v) - \text{val}(u) \ge 0 at each position, and let 1≤k≤∑D1 \le k \le \sum D. Then KTH(u,v,k)(u, v, k) returns the least pp with ∑j≤pD[j]≥k\sum_{j \le p} D[j] \ge k.

Proof. Line K1 rejects other kk, using Lemma 1 for ∑D=v.sum−u.sum\sum D = v.sum - u.sum. Let p∗p^* be the answer. We claim that at the test of line K2 the nodes uu and vv are well formed for the current [lo,hi)[lo, hi), p∗∈[lo,hi)p^* \in [lo, hi), and kk equals k0−∑j<loD[j]k_0 - \sum_{j < lo} D[j] for the original k0k_0, with 1≤k≤∑lo≤j<hiD[j]1 \le k \le \sum_{lo \le j < hi} D[j]. This holds at the start. In a step, let cc be the value of line K3; by Lemma 1, c=∑lo≤j<midD[j]c = \sum_{lo \le j < mid} D[j]. If k≤ck \le c, then the kk-th unit after position lo−1lo - 1 lies in [lo,mid)[lo, mid), so p∗<midp^* < mid, and line K4 keeps the claim with kk unchanged. If k>ck > c, then p∗≥midp^* \ge mid, and line K5 subtracts the cc units of [lo,mid)[lo, mid), which keeps k=k0−∑j<midD[j]k = k_0 - \sum_{j < mid} D[j] and 1≤k1 \le k. Each step halves the range, so the loop ends with hi−lo=1hi - lo = 1, and then p∗=lop^* = lo, which line K6 returns. ■\blacksquare

Back to Persistent segment tree