Introduction to line segment tree algorithm

Posted by Integra_grrl on Tue, 04 Jan 2022 02:32:59 +0100

What is a segment tree

Segment tree is a binary search tree. With the idea of divide and conquer algorithm, it divides an interval into some unit intervals, and each unit interval corresponds to a leaf node in the segment tree----------------- Adapted from Baidu Encyclopedia

Function of line segment tree

Question: Here you are n n n unordered numbers, q q q operations, asking for a certain interval [ l , r ] [l, r] The sum of [l,r] or modify the value of this interval. assume 1 < = n , q < = 100 , 000 1 <= n, q <= 100,000 1 < = n, Q < = 100000, the time limit for you is 1 s 1s 1s.
If violence is used? It is assumed that the interval of each modification and query is [ 1 , n ] [1, n] [1,n], then the worst time complexity needs O ( q ∗ n ) O(q*n) O(q * n), obviously timeout.
At this time, we need to use the magical line segment tree.

Observe its structure:

Achievements:

By observing the law of node subscript, it can be concluded that the subscripts of the two son nodes of the current node p are p ∗ 2 p*2 p * 2 and p ∗ 2 + 1 p*2 +1 p∗2+1.
Recursive tree building is similar to merge sorting. Time complexity O ( n ∗ l o g 2 ( n ) ) O (n*log2(n)) O(n∗log2(n))

void build(int l, int r, int p){
	if(l ==r){
		tree [p] = a[l]; //Leaf node information
		return ;
	}
	int mid = (l + r) >> 1;	// Equivalent to mid = (l + r) / 2,
	build(l, mid, p << 1); //Left bound interval
	build(mid + i, r, p << 1|1); //In the right section, P < 1 | 1 is equivalent to p / 2 + 1
	tree[p] = tree[p << 1] + tree[p << 1|1]; //Upload son node information
}

Query the information of interval [L,R]:

Assume that every time you access a node p p p is an interval [ l , r ] [l, r] [l,r], m i d = ( l + r ) / 2 mid = (l + r)/ 2 mid=(l+r)/2
If [ l , r ] [l, r] [l,r] happens to be [ L , R ] [L,R] [L,R] contains (i.e L ≤ l L≤l L≤l && r ≤ R ) r ≤ R) r ≤ R), then p p We all need the information of node p.
In other cases, judgment is required m i d mid mid and L , R L,R 50. Relationship of R:
1. If L ≤ m i d L ≤ mid L ≤ mid, indicating that we need some information of its left interval.
2. If R > m i d R > mid R> Mid, indicating that we need some information of its right interval· Finally, all the information sets of the above situations are returned.

int query(int l, int r, int p, int L, int R){
	if (L <= l && r <= R) return tree[p]; //If the interval contains, all information of the node will be returned
	int mid = (l + r) >> 1, ret = 0;// ret is used to save information
	if (L <= mid) ret += query(l, mid, p << 1, L, R);//Take part of the information of the left section
	if (R > mid) ret += query(mid + 1, r, p << 1|1,L, R);//Take part of the information of the right section
	return ret;//Return the above results
}

To modify a location k's information:

Very simple, just find and modify the information of the corresponding leaf node.

void update(int l, int r, int p, int k, int s) {
	if(l == r){
		tree[p] = s;
		return ;
	}
	int mid = (l + r) >> 1;
	if(k <= mid) update(l, mid, p << 1, k, s); // k < = mid indicates that k is in the left interval
	else update(mid + 1, r, p << 1 | 1,k, s); // Otherwise, it is in the right interval
	tree[p] = tree[p << 1]+ tree[p << 1|1]; // Don't forget to update the information of the p node~
}

The time complexity is O ( l o g 2 ( n ) ) O(log2(n)) O(log2(n))

Modify interval [L,R]

Here's the problem. What if the interval [L,R] modifies the information?
Continue the idea of violence, [L,R] one by one.
Then the time complexity of one modification is O ( n ∗ l o g 2 ( n ) ) O(n*log2(n)) O(n * log2(n)), modified q q q times
Time complexity up to O ( q ∗ n ∗ l o g 2 ( n ) ) O (q*n*log2(n)) O(q * n * log2(n)), but not as simple violence?
Can it be converted into query time complexity?
That is, we only need to modify the nodes that can be queried by interval query.
For example, total interval [ 1 , 10 ] [1,10] [1,10], to modify [ 4 , 9 ] [4,9] [4,9] interval, we only need to modify it [ 4 , 5 ] [4,5] [4,5], [ 6 , 8 ] [6,8] [6,8], [ 9 , 9 ] [9,9] The information of the node where [9,9] is located.
What about the child nodes of these nodes?
The modified interval is marked with lazy. If the sub interval of this interval is accessed next time, this mark will be lowered.

Magic lazy tag for interval modification. Time complexity optimized to O ( l o g 2 ( n ) ) O(log2(n)) O(log2(n))

void update(int l, int r,int p, int L, int R, int s) {
	if(L <= l && r <= R) { //If the current access interval is included in the interval to be accessed
		tree[p] += (r - l + 1) * s; //First, update the value of the current node
		lazy[p] += s; //Label with lazy
		return ;
	}
	int mid = (l + r) >> 1;
	if(lazy[p] != 0){ //If the current interval is marked with lazy, it needs to be lowered
		tree[p << 1] += (mid - l + 1) * lazy[p];
		tree[p << 1 | 1] += (r - mid) * lazy[p];  
		//First update the values of the two child nodes
		lazy[p << 1] += lazy[p];
		lazy[p << 1 | 1] += lazy[p]; 
		//Drop tag to two child nodes
		lazy[p] = 0; //The current node is unmarked
	}
	if(L <= mid) update(l, mid, p << 1, L, R, s);
	// L < = mid indicates that the left interval needs to be updated
	if(R > mid)update(mid + 1, r, p << 1 | 1,L,R, s);
	// R > mid indicates that the right interval needs to be updated
	tree[p] = tree[p << 1] + tree[p << 1|1];
	// Don't forget to update the information of the p node
}

Magic lazy tag for interval modification. Time complexity optimized to O ( l o g 2 ( n ) ) O (log2(n)) O(log2(n))

int query (int l, int r, int p, int L, int R){
	if(L <= l && r <= R) return tree[p];
	// If the interval contains, all information of the node will be returned
	int mid = (l + r) >> 1, ret = 0; // ret is used to save information
	if(lazy [p] != 0) {
	//If the current interval is marked with lazy, it needs to be lowered
		tree[p << 1] += (mid - l + 1) * lazy[p];
		tree[p << 1|1] += (r - mid) * lazy[p];//First update the values of the two child nodes
		lazy[p << 1] += lazy[p];
		lazy[p << 1|1] += lazy[p];//Drop tag to two child nodes
		lazy[p] = 0;//The current node is unmarked
	}
	if(L <= mid) ret += query(l, mid, p << 1, L, R);
	//Take part of the information of the left section
	if(R > mid) ret += query(mid + 1, r, p << 1|1, L,R);
	//Take part of the information of the right section
	return ret;//Return the above results
}

Summary & Precautions:

Tree building time complexity O ( n ∗ l o g 2 ( n ) ) O (n*log2(n)) O(n∗log2(n))
Single point / interval update / query time complexity O ( l o g 2 ( n ) ) O(log2(n)) O(log2(n))
The time complexity is extremely excellent, and the space needs to be opened 4 ∗ n 4* n 4∗n
It is used to process some interval queries and modifications that require divisible interval information