给定一个由N 个整数组成的数组arr[] 。任务是执行以下操作:
- 将值X添加到从索引A到B 的所有元素,其中0 ≤ A ≤ B ≤ N-1 。
- 求从索引L到R的元素的总和,其中0 ≤ L ≤ R ≤ N-1在给上述数组的更新之前和之后。
例子:
Input: arr[] = {1, 3, 5, 7, 9, 11}, L = 1, R = 3, A = 1, B = 5, X = 10
Output:
Sum of values in given range = 15
Updated sum of values in given range = 45
Explanation:
Sum of values in the range 1 to 3 is 3 + 5 + 7 = 15.
arr[] after adding 10 from index 1 to 5 is arr[] = {1, 13, 15, 17, 19, 21}
Sum of values in the range 1 to 3 after update is 13 + 15 + 17 = 45.
Input: arr[] = { 11, 32, 5, 7, 19, 11, 8}, L = 2, R = 6, A = 1, B = 5, X = 16
Output:
Sum of values in given range = 50
Updated sum of values in given range = 114
Explanation:
Sum of values in the range 2 to 6 is 5 + 7 + 19 + 11 + 8 = 50.
arr[] after adding 16 from index 1 to 5 is arr[] = {11, 48, 21, 23, 35, 27, 8}
Sum of values in the range 2 to 6 after update is 21 + 23 + 35 + 27 + 8 = 114.
方法:
本文讨论了针对给定问题使用 Segment Tree 的递归方法。在这篇文章中,我们将讨论一种使用堆栈数据结构来避免递归的方法。
以下是使用 Stack 实现 Segment Tree 的步骤:
- 这个想法是使用元组来存储堆栈中具有节点编号和范围索引的状态。
- 对于构建段树:
- 将根节点作为元组推入堆栈:
Stack S;
start = 0, end = arr_size - 1
S.emplace(1, start, end)
- 从堆栈中弹出元素并执行以下操作直到堆栈变空:
- 如果起始索引等于结束索引,那么我们到达叶节点并将 Segment 树数组中的值更新为给定数组中当前索引处的值。
- 否则插入带有当前节点的标志元组作为S.emplace(current_node, INF, INF)以反转评估顺序并插入具有左右孩子值的元组:
mid = (start + end) / 2
st.emplace(curr_node * 2, start, mid)
st.emplace(curr_node * 2 + 1, mid + 1, end)
- 如果开始索引和结束索引与INF相同,则将当前索引处的 Segment Tree 值更新为:
Value at current index is updated as value at left child and right child:
tree[curr_node] = tree[2*curr_node] + tree[2*curr_node + 1]
- 对于更新树:
- 将根节点推送到堆栈,就像构建 Segment Tree 一样。
- 从堆栈中弹出元素并执行以下操作直到堆栈变空:
- 如果当前节点有任何挂起的更新,则首先更新到当前节点。
- 如果当前节点范围完全在更新查询范围内,则使用该值更新当前节点。
- 如果当前节点范围与更新查询范围重叠,则按照上述方法将左孩子和右孩子的元组推送到堆栈中。
- 使用上面左右孩子的结果更新查询。
- 对于更新查询:
- 将根节点推送到堆栈,就像构建 Segment Tree 一样。
- 从堆栈中弹出元素并执行以下操作直到堆栈变空:
- 如果当前节点范围位于给定查询之外,则继续下一次迭代。
- 如果当前节点范围完全在更新查询范围内,则用当前节点值更新结果。
- 如果当前节点范围与更新查询范围重叠,则按照上述方法将左孩子和右孩子的元组推送到堆栈中。
- 使用从左右子节点获得的值更新结果。
下面是上述方法的实现:
CPP
#include"bits/stdc++.h"
using namespace std;
constexpr static int MAXSIZE = 1000;
constexpr static int INF
= numeric_limits::max();
// Segment Tree array
int64_t tree[MAXSIZE];
// Lazy Update array
int64_t lazy[MAXSIZE];
// This tuple will hold tree state
// the stacks
using QueryAdaptor
= tuple;
// Build our segment tree
void build_tree(int64_t* arr,
int64_t arr_size)
{
// Stack will use to update
// the tree value
stack st;
// Emplace the root of the tree
st.emplace(1, 0, arr_size - 1);
// Repeat until empty
while (!st.empty()) {
// Take the indexes at the
// top of the stack
int64_t currnode, curra, currb;
// value at the top of the
// stack
tie(currnode, curra, currb) = st.top();
// Pop the value from the
// stack
st.pop();
// Flag with INF ranges are merged
if (curra == INF && currb == INF) {
tree[currnode] = tree[currnode * 2]
+ tree[currnode * 2 + 1];
}
// Leaf node
else if (curra == currb) {
tree[currnode] = arr[curra];
}
else {
// Insert flag node inverse
// order of evaluation
st.emplace(currnode, INF, INF);
int64_t mid = (curra + currb) / 2;
// Push children
st.emplace(currnode * 2,
curra, mid);
st.emplace(currnode * 2 + 1,
mid + 1, currb);
}
}
}
// A utility function that propagates
// updates lazily down the tree
inline void push_down(int64_t node,
int64_t a,
int64_t b)
{
if (lazy[node] != 0) {
tree[node] += lazy[node] * (b - a + 1);
if (a != b) {
lazy[2 * node] += lazy[node];
lazy[2 * node + 1] += lazy[node];
}
lazy[node] = 0;
}
}
// Iterative Range_Update function to
// add val to all elements in the
// range i-j (inclusive)
void update_tree(int64_t arr_size,
int64_t i,
int64_t j,
int64_t val)
{
// Initialize the stack
stack st;
// Emplace the root of the tree
st.emplace(1, 0, arr_size - 1);
// Work until empty
while (!st.empty()) {
// Take the indexes at the
// top of the stack
int64_t currnode, curra, currb;
tie(currnode, curra, currb) = st.top();
st.pop();
// Flag with INF ranges are merged
if (curra == INF && currb == INF) {
tree[currnode] = tree[currnode * 2]
+ tree[currnode * 2 + 1];
}
// Traverse the previous updates
// down the tree
else {
push_down(currnode, curra, currb);
// No overlap condition
if (curra > currb || curra > j
|| currb < i) {
continue;
}
// Total overlap condition
else if (curra >= i && currb <= j)
{
// Update lazy array
tree[currnode] += val * (currb - curra + 1);
if (curra != currb) {
lazy[currnode * 2] += val;
lazy[currnode * 2 + 1] += val;
}
}
// Partial Overlap
else
{
// Insert flag node inverse
// order of evaluation
st.emplace(currnode, INF, INF);
int64_t mid = (curra + currb) / 2;
// Push children
st.emplace(currnode * 2,
curra, mid);
st.emplace(currnode * 2 + 1,
mid + 1, currb);
}
}
}
}
// A function that find the sum of
// all elements in the range i-j
int64_t query(int64_t arr_size,
int64_t i,
int64_t j)
{
// Initialize stack
stack st;
// Emplace root of the tree
st.emplace(1, 0, arr_size - 1);
int64_t result = 0;
while (!st.empty())
{
// Take the indexes at the
// top of the stack
int64_t currnode, curra, currb;
tie(currnode, curra, currb) = st.top();
st.pop();
// Traverse the previous updates
// down the tree
push_down(currnode, curra, currb);
// No overlap
if (curra > currb || curra > j
|| currb < i) {
continue;
}
// Total Overlap
else if (curra >= i && currb <= j) {
result += tree[currnode];
}
// Partial Overlap
else {
std::int64_t mid = (curra + currb) / 2;
// Push children
st.emplace(currnode * 2,
curra, mid);
st.emplace(currnode * 2 + 1,
mid + 1, currb);
}
}
return result;
}
// Driver Code
int main()
{
// Initialize our trees with 0
memset(tree, 0, sizeof(int64_t) * MAXSIZE);
memset(lazy, 0, sizeof(int64_t) * MAXSIZE);
int64_t arr[] = { 1, 3, 5, 7, 9, 11 };
int n = sizeof(arr) / sizeof(arr[0]);
// Build segment tree from given array
build_tree(arr, n);
// Print sum of values in array
// from index 1 to 3
cout << "Sum of values in given range = "
<< query(n, 1, 3)
<< endl;
// Add 10 to all nodes at indexes
// from 1 to 5
update_tree(n, 1, 5, 10);
// Find sum after the value is updated
cout << "Updated sum of values in given range = "
<< query(n, 1, 3)
<< endl;
return 0;
}
Java
// Java implementation of the approach
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
class GFG
{
static final int MAXSIZE = 1000;
static final int INF = (int) Double.POSITIVE_INFINITY;
// Segment Tree array
static int[] tree = new int[MAXSIZE];
// Lazy Update array
static int[] lazy = new int[MAXSIZE];
// Build our segment tree
static void build_tree(int[] arr, int arr_size)
{
// Stack will use to update
// the tree value
Stack> st = new Stack<>();
// push the root of the tree
st.push(Arrays.asList(1, 0, arr_size - 1));
// Repeat until empty
while (!st.isEmpty())
{
// Take the indexes at the
// top of the stack
int currnode, curra, currb;
// value at the top of the
// stack
List temp = st.peek();
currnode = temp.get(0);
curra = temp.get(1);
currb = temp.get(2);
// Pop the value from the
// stack
st.pop();
// Flag with INF ranges are merged
if (curra == INF && currb == INF)
{
tree[currnode] = tree[currnode * 2] +
tree[currnode * 2 + 1];
}
// Leaf node
else if (curra == currb)
{
tree[currnode] = arr[curra];
}
else {
// Insert flag node inverse
// order of evaluation
st.push(Arrays.asList(currnode, INF, INF));
int mid = (curra + currb) / 2;
// Push children
st.push(Arrays.asList(currnode * 2, curra, mid));
st.push(Arrays.asList(currnode * 2 + 1, mid + 1, currb));
}
}
}
// A utility function that propagates
// updates lazily down the tree
static void push_down(int node, int a, int b)
{
if (lazy[node] != 0)
{
tree[node] += lazy[node] * (b - a + 1);
if (a != b)
{
lazy[2 * node] += lazy[node];
lazy[2 * node + 1] += lazy[node];
}
lazy[node] = 0;
}
}
// Iterative Range_Update function to
// add val to all elements in the
// range i-j (inclusive)
static void update_tree(int arr_size, int i,
int j, int val)
{
// Initialize the stack
Stack> st = new Stack<>();
// push the root of the tree
st.push(Arrays.asList(1, 0, arr_size - 1));
// Work until empty
while (!st.isEmpty())
{
// Take the indexes at the
// top of the stack
int currnode, curra, currb;
List temp = st.peek();
currnode = temp.get(0);
curra = temp.get(1);
currb = temp.get(2);
st.pop();
// Flag with INF ranges are merged
if (curra == INF && currb == INF)
{
tree[currnode] = tree[currnode * 2] +
tree[currnode * 2 + 1];
}
// Traverse the previous updates
// down the tree
else
{
push_down(currnode, curra, currb);
// No overlap condition
if (curra > currb || curra > j || currb < i)
{
continue;
}
// Total overlap condition
else if (curra >= i && currb <= j)
{
// Update lazy array
tree[currnode] += val * (currb - curra + 1);
if (curra != currb)
{
lazy[currnode * 2] += val;
lazy[currnode * 2 + 1] += val;
}
}
// Partial Overlap
else
{
// Insert flag node inverse
// order of evaluation
st.push(Arrays.asList(currnode, INF, INF));
int mid = (curra + currb) / 2;
// Push children
st.push(Arrays.asList(currnode * 2, curra, mid));
st.push(Arrays.asList(currnode * 2 + 1,
mid + 1, currb));
}
}
}
}
// A function that find the sum of
// all elements in the range i-j
static int query(int arr_size, int i, int j)
{
// Initialize stack
Stack> st = new Stack<>();
// push root of the tree
st.push(Arrays.asList(1, 0, arr_size - 1));
int result = 0;
while (!st.isEmpty())
{
// Take the indexes at the
// top of the stack
int currnode, curra, currb;
List temp = st.peek();
currnode = temp.get(0);
curra = temp.get(1);
currb = temp.get(2);
st.pop();
// Traverse the previous updates
// down the tree
push_down(currnode, curra, currb);
// No overlap
if (curra > currb || curra > j || currb < i)
{
continue;
}
// Total Overlap
else if (curra >= i && currb <= j)
{
result += tree[currnode];
}
// Partial Overlap
else
{
int mid = (curra + currb) / 2;
// Push children
st.push(Arrays.asList(currnode * 2, curra, mid));
st.push(Arrays.asList(currnode * 2 + 1, mid + 1, currb));
}
}
return result;
}
// Driver Code
public static void main(String[] args)
{
// Initialize our trees with 0
Arrays.fill(tree, 0);
Arrays.fill(lazy, 0);
int arr[] = { 1, 3, 5, 7, 9, 11 };
int n = arr.length;
// Build segment tree from given array
build_tree(arr, n);
// Print sum of values in array
// from index 1 to 3
System.out.printf("Sum of values in given range = %d\n", query(n, 1, 3));
// Add 10 to all nodes at indexes
// from 1 to 5
update_tree(n, 1, 5, 10);
// Find sum after the value is updated
System.out.printf("Updated sum of values in given range = %d\n", query(n, 1, 3));
}
}
// This code is contributed by sanjeev2552
Sum of values in given range = 15
Updated sum of values in given range = 45
时间复杂度:
- 对于树构造: O(N),树中有 (2n-1) 个节点,每个节点的值计算一次。
- 对于查询: O(log N),为了查询一个总和,我们在每个级别最多处理四个节点,级别数为 log N。
- 对于更新: O(log N),使用惰性传播更新树是 O(Log N),因为我们更新树的根,然后只更新树的范围在每个级别重叠的部分。
如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live