给定一个N个整数的数组arr [] 。任务是执行以下操作:
- 向从索引A到B的所有元素添加值X ,其中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.
方法:
本文讨论了针对给定问题使用分段树的递归方法。在这篇文章中,我们将讨论一种使用堆栈数据结构来避免递归的方法。
以下是使用Stack实施细分树的步骤:
- 想法是使用元组在堆栈中存储具有节点号和范围索引的状态。
- 对于构建段树:
- 将根节点作为元组推入堆栈:
Stack S;
start = 0, end = arr_size - 1
S.emplace(1, start, end)
- 从堆栈中弹出元素,然后执行以下操作,直到堆栈变空:
- 如果起始索引等于终止索引,那么我们到达叶节点,并将Segment Tree数组的值更新为给定数组中当前索引的值。
- 否则,将带有当前Node的标志元组插入为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]
- 对于更新树:
- 将根节点压入堆栈,以完成构建段树的操作。
- 从堆栈中弹出元素,然后执行以下操作,直到堆栈变空:
- 如果当前节点有任何待处理的更新,则首先更新到当前节点。
- 如果当前节点范围完全位于更新查询范围内,则使用该值更新当前节点。
- 如果当前节点范围与更新查询范围重叠,则遵循上述方法,并在堆栈中推送用于左子级和右子级的元组。
- 使用上面的左,右子项的结果更新查询。
- 对于更新查询:
- 将根节点压入堆栈,以完成构建段树的操作。
- 从堆栈中弹出元素,然后执行以下操作,直到堆栈变空:
- 如果当前节点范围不在给定查询范围内,则继续下一次迭代。
- 如果当前节点范围完全位于更新查询范围内,则使用当前节点值更新结果。
- 如果当前节点范围与更新查询范围重叠,则遵循上述方法,并在堆栈中推送用于左子级和右子级的元组。
- 使用从左右子Node获得的值更新结果。
下面是上述方法的实现:
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)
{
// Intialize 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)
{
// Intialize 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)
{
// Intialize 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)
{
// Intialize 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),要查询总和,我们在每个级别处理了至少四个节点,级别数为logN。
- 对于更新: O(log N),要更新具有延迟传播的树是O(Log N),因为我们更新树的根,然后仅更新树的范围在每个级别重叠的那部分。