Line segment tree interval maximum value query / interval update simple implementation

Line segment tree definition

Line segment = one-dimensional array
As shown in the figure: according to the subscript, a one-dimensional array is divided into two halves, each line segment tree node represents a sub-interval in the original array , and the leaf node represents a length of 1. Interval
Insert picture description here

What can a line tree do

Mainly to solve the interval problem, such as querying the maximum value of an interval, adding or subtracting a number to all elements of an interval, or finding the sum of a certain interval. For static intervals, we have better methods, such as [ prefix Sum and difference array ], [ ST do RMQ static query ]

But these methods have disadvantages: what if our array is updated dynamically? That is, query while updating.

  • Segment tree can dynamically update interval, while faster query refers to O (log (n))

Line tree structure

Based on a lot of features above, the nodes of a line segment tree need to have:

  • What is currently represented
  • Left and right children
  • The stored value (such as the maximum value of the currently represented interval)

If you use an array (that is, a complete binary tree) to store, you may have to add

  • Subscript in array

You can quickly write the definition of the structure

typedef struct node
{
	int l, r, i, val;
}node;

In fact, it doesn't need to be so troublesome, just save [val], that is, the value stored in the line tree node can be stored in a one-dimensional array

We use an array to simulate a complete binary tree to store nodes, but because of the bipartite nature, we can directly push out the sub-range represented by the current node whether it is searching or updating , so storage only needs to store the value of the node. Point, because if it is full, the actual number of nodes is 1 + 2 + 4 + 8 + ... + n/2 + n, that is , full binary tree

int tree[maxn*4];

Segment tree creation

Use an array to save the nodes. If you are familiar with the binary tree, you will be quick
Insert picture description here
. The subscript of the current node in the array is i, then the left child subscript 2*iand the right child subscript2*i+1

Also recursively created, the original array with known a[]conservation, trees[]an array of values stored node, the value range of the node that is the representative of the largest element of

Set the current intervals are to be created [l, r], tree array isubscript represent this range

  • If l = r, then tree[i] = a[l]terminate the recursion
  • Otherwise, recurse the left and right subtrees, that is, create two halves of subintervals, and their subscripts in the tree:, 2*i 与 2*i+1update the i subscript after the recursion ends, ietree[i] = max(tree[2*i], tree[2*i+1])
/*
i : 当前线段树节点在tree数组中的下标
l : 当前线段树节点代表的区间的左端点
r : 当前线段树节点代表的区间的右端点
*/
void creat(int i, int l, int r)
{
	if(l==r) {tree[i]=a[l]; return;}
	creat(2*i, l, (l+r)/2), creat(2*i+1, (l+r)/2+1, r);
	tree[i] = max(tree[2*i], tree[2*i+1]);
}

Line segment query

A query interval, because we hold some values subdivision two intervals (ie, the presence of trees in the array), so long as to find a certain range, so that these sections are connected together to look for the interval equal to [] , and then merge the answers to these intervals can be , Still use recursive query, and we can always find a few suitable intervals, because the minimum interval is the length of 1, if you ca n’t do it, you can just stick it out.

Suppose the query interval is Q. We only need to find all the sub-intervals of Q, and then merge the answers. This process uses recursion.

  • If the interval represented by the current recursive node does not intersect with Q , then stop recursion and return the minimum value
  • If the interval represented by the current recursive node is a sub-interval of Q , then stop recursion and return the value stored in the current interval
  • Otherwise, the interval represented by the current recursive node is divided in half, and the left and right sub-intervals are recursive
/*
ql : 要查询的区间左端点
qr :要查询的区间右端点
i  : 当前线段树节点在tree数组中的下标
l  : 当前线段树节点代表的区间左端点
r  : 当前线段树节点代表的区间右端点
*/
int query(int ql, int qr, int i, int l, int r)
{
	if(qr<l || r<ql) return INT_MIN;
	if(ql<=l && r<=qr) return tree[i];
	return max(query(ql, qr, 2*i, l, (l+r)/2), 
			   query(ql, qr, 2*i+1, (l+r)/2+1, r));
}

Line segment tree interval update

Updating all numbers in an interval , the simple complexity nlog (n), is a bit slow. We introduced a caching mechanism that caches all the numbers in the current interval . When we query, we can use this number To do addition and subtraction

int buffer[maxn*4];

But the problem is that if you perform multiple update operations, and these update intervals are different, and even there is an intersection, then the situation becomes troublesome

For updating an interval Q, if the number v to be updated in this interval is known to be cached, then the result of the maximum value of this interval will be added to v, and we directly modify the value stored in the tree array, that is tree[i]+=v, so we can immediately Update to the maximum value of this interval, and then throw the problem to the two sub-intervals of the next layer , that is, the cache of the left and right sub-intervals has been increased!

The advantage of this is that we need a small number of operations to complete the modification of an interval, and the rest of the operations are left to the query to "do it"

It is worth mentioning that

  • When updating, the value to be increased can be cached only when the current interval is a sub-interval of Q , and then the question is thrown to the sub-interval of the next layer, and the answer of this layer is updated at the same time, and then returned
  • If you need to recurse to the next layer when updating, do n’t forget to throw the cache of this layer first, and then operate
  • After recursive call update, remember to update the value of this layer before return
  • Before querying, always remember to clear the cache of the current interval, ie throw the problem to the next layer
/*
清空在tree数组中i下标代表的区间的缓存,向下一层抛出缓存
*/
void clear_buf(int i)
{
	// 更新当前节点存储的值 
	tree[i] += buffer[i];
	// 将缓存传递到下一层的节点 
	buffer[2*i] += buffer[i];
	buffer[2*i+1] += buffer[i];
	// 清空当前节点缓存 
	buffer[i] = 0;
}

/*
ul  : 要更新的区间左端点
ur  : 要更新的区间右端点
i   : 当前区间的值存储在tree数组i下标
l   : 当前区间左端点
r   : 当前区间右端点
val : 要更新的值
*/
void update(int ul, int ur, int i, int l, int r, int val)
{
	if(ur<l || r<ul) return;
	if(ul<=l && r<=ur)
	{
		buffer[i] += val;
		clear_buf(i);
		return;
	}
	clear_buf(i);
	update(ul, ur, 2*i, l, (l+r)/2, val);
	update(ul, ur, 2*i+1, (l+r)/2+1, r, val);
	tree[i] = max(tree[2*i], tree[2*i+1]);
}

At the same time, the query function should also remember to throw down the cache

int query(int ql, int qr, int i, int l, int r)
{
	if(qr<l || r<ql) return INT_MIN;
	clear_buf(i);
	if(ql<=l && r<=qr) return tree[i];
	return max(query(ql, qr, 2*i, l, (l+r)/2), 
			   query(ql, qr, 2*i+1, (l+r)/2+1, r));
}

Code

#include <bits/stdc++.h>

using namespace std;

#define maxn 1000

int tree[maxn*4];
int buffer[maxn*4];
int a[maxn], n;

void creat(int i, int l, int r)
{
	if(l==r) {tree[i]=a[l]; return;}
	creat(2*i, l, (l+r)/2), creat(2*i+1, (l+r)/2+1, r);
	tree[i] = max(tree[2*i], tree[2*i+1]);
}

void clear_buf(int i)
{
	// 更新当前节点存储的值 
	tree[i] += buffer[i];
	// 将缓存传递到下一层的节点 
	buffer[2*i] += buffer[i];
	buffer[2*i+1] += buffer[i];
	// 清空当前节点缓存 
	buffer[i] = 0;
}

int query(int ql, int qr, int i, int l, int r)
{
	if(qr<l || r<ql) return INT_MIN;
	clear_buf(i);
	if(ql<=l && r<=qr) return tree[i];
	return max(query(ql, qr, 2*i, l, (l+r)/2), 
			   query(ql, qr, 2*i+1, (l+r)/2+1, r));
}

void update(int ul, int ur, int i, int l, int r, int val)
{
	if(ur<l || r<ul) return;
	if(ul<=l && r<=ur)
	{
		buffer[i] += val;
		clear_buf(i);
		return;
	}
	clear_buf(i);
	update(ul, ur, 2*i, l, (l+r)/2, val);
	update(ul, ur, 2*i+1, (l+r)/2+1, r, val);
	tree[i] = max(tree[2*i], tree[2*i+1]);
}

int main()
{	
	memset(buffer, 0, sizeof(buffer)); 
	cin>>n;
	for(int i=1; i<=n; i++) cin>>a[i];
	creat(1, 1, n);
	
	update(1, 4, 1, 1, n, 10);
	update(3, 4, 1, 1, n, 10);
	
	for(int l=1; l<=n; l++)
		for(int r=l; r<=n; r++)
			cout<<l<<" "<<r<<" "<<query(l, r, 1, 1, n)<<endl;
	
	return 0;
}

/*
8
1 2 3 4 5 6 7 8
*/
8
1 2 3 4 5 6 7 8
[1, 1] 11
[1, 2] 12
[1, 3] 23
[1, 4] 24
[1, 5] 24
[1, 6] 24
[1, 7] 24
[1, 8] 24
[2, 2] 12
[2, 3] 23
[2, 4] 24
[2, 5] 24
[2, 6] 24
[2, 7] 24
[2, 8] 24
[3, 3] 23
[3, 4] 24
[3, 5] 24
[3, 6] 24
[3, 7] 24
[3, 8] 24
[4, 4] 24
[4, 5] 24
[4, 6] 24
[4, 7] 24
[4, 8] 24
[5, 5] 5
[5, 6] 6
[5, 7] 7
[5, 8] 8
[6, 6] 6
[6, 7] 7
[6, 8] 8
[7, 7] 7
[7, 8] 8
[8, 8] 8
Published 262 original articles · won 11 · 10 thousand views

Guess you like

Origin blog.csdn.net/weixin_44176696/article/details/105187658