BuringStraw

BuringStraw

Persistent Segment Tree Template

Wrote a persistent segment tree because I needed to use a treap in a simulated contest, but I had never written one before...
I wrote it based on my memory of the courses I took back then, because I couldn't understand many blogs I read

Overview#

Whenever there is a modification operation, copy the node that needs to be modified and perform the modification on the copied node. This modification also includes modifications to the connection structure between points. Then, store the root node of each version in the root array.

Structure#

This segment tree needs to dynamically allocate nodes, so we need a struct

struct node {
	int l, r; // corresponding interval
	int ch[2]; // 0: left child, 1: right child
	int v; // value
} c[MAXN];

Then we need an array to store the initial elements and the root array

int a[MAXN], root[MAXN];

We also need to keep track of the latest node and the latest version

int newp, newv;

Operations#

In all operations, pay attention to the fact that newp will change after recursive operations.

Building the Tree#

Similar to a regular segment tree
First, initialize the l and r of the root node

root[++newv] = ++newp;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
	scanf("%d", a + i);
}
c[newp].l = 1;
c[newp].r = n;
void build (int p) {
	if (c[p].l == c[p].r) {
		c[p].v = a[c[p].l];
	}
	else {
		++newp;
		int son = newp; // save the current node. It cannot be used after newp changes
		c[son].l = c[p].l;
		c[son].r = (c[p].l + c[p].r) >> 1;
		c[p].ch[0] = son;
		build(son);
		c[p].v += c[son].v;
		++newp;
		son = newp;
		c[son].l = ((c[p].l + c[p].r) >> 1) + 1;
		c[son].r = c[p].r;
		c[p].ch[1] = son;
		build(son);
		c[p].v += c[son].v;
	}
}

Querying#

Similar to a regular segment tree
Split into two functions very elegantly

int rq(int p, int l, int r) {
	if (c[p].l >= l && c[p].r <= r) { // completely within the range
		return c[p].v;
	}
	else if (c[p].r < l || c[p].l > r) { // completely outside the range
		return 0;
	}
	else {
		int s = 0;
		s += rq(c[p].ch[0], l, r);
		s += rq(c[p].ch[1], l, r);
		return s;
	}
}

int query (int v, int l, int r) {
	return rq(root[v], l, r);
}

Modification#

The only difference from a regular segment tree

int ra (int p, int x, int k) { // return value: the newly created node after modification in the current version
	if (c[p].l == c[p].r && c[p].l == x) {
		c[++newp] = c[p];
		c[newp].v += k;
		return newp;
	}
	else {
		if (c[p].r < x || c[p].l > x) return p; // if there is no modification, continue to use the original node
		else {
            int son = ++newp;
			c[son] = c[p];
			c[son].ch[0] = ra(c[p].ch[0], x, k); // update its own children
			c[son].ch[1] = ra(c[p].ch[1], x, k);
			c[son].v = c[c[son].ch[0]].v + c[c[son].ch[1]].v;
			return son;
		}
	}
}

void add (int v, int x, int k) {
	int p = root[v];
	root[++newv] = ra(p, x, k); // root node of the new version
}

Others#

However, this cannot achieve interval modification.
But the template problem doesn't require it
If you want to perform interval modification, just use the lazytag as usual, and there is no need to create a new version when querying. It's very simple...
Also, every time I write such a long data structure, I always feel that it would look better if written with pointers.

Double Experience#

P3834 [Template] Persistent Segment Tree 1 (Treap)
P3919 [Template] Persistent Array (Persistent Segment Tree/Balanced Tree)
I got double experience because I used binary search to pass the treap problem

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.