📌  相关文章
📜  二进制索引树:范围更新和范围查询

📅  最后修改于: 2021-04-17 08:53:56             🧑  作者: Mango

给定一个数组arr [0..n-1]。需要执行以下操作。

  1. update(l,r,val) :将[val]从[l,r]添加到数组中的所有元素。
  2. getRangeSum(l,r) :从[l,r]中查找数组中所有元素的总和。

最初,数组中的所有元素均为0。查询可以按任何顺序进行,即,在范围求和之前可以进行许多更新。

例子:

Input : n = 5   // {0, 0, 0, 0, 0}
Queries: update : l = 0, r = 4, val = 2
         update : l = 3, r = 4, val = 3 
         getRangeSum : l = 2, r = 4

Output: Sum of elements of range [2, 4] is 12

Explanation : Array after first update becomes
              {2, 2, 2, 2, 2}
              Array after second update becomes
              {2, 2, 2, 5, 5}

在上一篇文章中,我们讨论了使用BIT的范围更新和点查询解决方案。
rangeUpdate(l,r,val):我们在元素的索引“ l”处添加“ val”。我们从索引为“ r + 1”的元素中减去“ val”。
getElement(index)[或getSum()]:我们将元素的总和从0返回到可以使用BIT快速获得的索引。

我们可以使用getSum()查询来计算rangeSum()。
rangeSum(l,r)= getSum(r)– getSum(l-1)

一个简单的解决方案是使用上一篇文章中讨论的解决方案。范围更新查询是相同的。范围总和查询可以通过对范围内的所有元素进行获取查询来实现。

一个有效的解决方案是确保两个查询都可以在O(Log n)时间内完成。我们使用前缀和获得范围和。如何确保以某种方式完成更新,以便可以快速完成前缀总和?考虑以下情况:在范围[l,r]上进行范围更新之后,需要前缀总和[0,k](其中0 <= k

情况1 :0 更新查询不会影响总和查询。

情况2 :l <= k <= r
考虑一个例子:

Add 2 to range [2, 4], the resultant array would be:
0 0 2 2 2
If k = 3
Sum from [0, k] = 4

如何得到这个结果?
只需将第l索引的val添加到k索引。更新查询后,总和增加“ val *(k)– val *(l-1)”。

情况3 :k> r
对于这种情况,我们需要将l索引的“ val”添加到r索引。由于更新查询,总和增加“ val * r – val *(l-1)”。

观察结果:
情况1:很简单,因为总和将保持与更新前相同。

情况2:总和增加val * k – val *(l-1)。我们可以找到“ val”,类似于在范围更新和点查询文章中找到第i元素。因此,我们为范围更新和点查询保留一个BIT,此BIT将有助于找到第k索引的值。现在计算val * k,如何处理多余项val *(l-1)?
为了处理这个额外的期限,我们保留了另一个BIT(BIT2)。在l索引处更新val *(l-1),因此对BIT2执行getSum查询时,结果将为val *(l-1)。

情况3:情况3的总和增加了“ val * r – val *(l-1)”,可以使用BIT2获得该项的值。代替添加,我们减去“ val *(l-1)– val * r”,因为我们可以像情况2一样通过添加val *(l-1)从BIT2中获得此值,并在每次更新中减去val * r手术。

Update Query 
Update(BITree1, l, val)
Update(BITree1, r+1, -val)
UpdateBIT2(BITree2, l, val*(l-1))
UpdateBIT2(BITree2, r+1, -val*r)

Range Sum 
getSum(BITTree1, k) *k) - getSum(BITTree2, k)

上述想法的实施

C++
// C++ program to demonstrate Range Update
// and Range Queries using BIT
#include 
using namespace std;
  
// Returns sum of arr[0..index]. This function assumes
// that the array is preprocessed and partial sums of
// array elements are stored in BITree[]
int getSum(int BITree[], int index)
{
    int sum = 0; // Initialize result
  
    // index in BITree[] is 1 more than the index in arr[]
    index = index + 1;
  
    // Traverse ancestors of BITree[index]
    while (index>0)
    {
        // Add current element of BITree to sum
        sum += BITree[index];
  
        // Move index to parent node in getSum View
        index -= index & (-index);
    }
    return sum;
}
  
// Updates a node in Binary Index Tree (BITree) at given
// index in BITree.  The given value 'val' is added to
// BITree[i] and all of its ancestors in tree.
void updateBIT(int BITree[], int n, int index, int val)
{
    // index in BITree[] is 1 more than the index in arr[]
    index = index + 1;
  
    // Traverse all ancestors and add 'val'
    while (index <= n)
    {
        // Add 'val' to current node of BI Tree
        BITree[index] += val;
  
        // Update index to that of parent in update View
        index += index & (-index);
    }
}
  
// Returns the sum of array from [0, x]
int sum(int x, int BITTree1[], int BITTree2[])
{
    return (getSum(BITTree1, x) * x) - getSum(BITTree2, x);
}
  
  
void updateRange(int BITTree1[], int BITTree2[], int n,
                 int val, int l, int r)
{
    // Update Both the Binary Index Trees
    // As discussed in the article
  
    // Update BIT1
    updateBIT(BITTree1,n,l,val);
    updateBIT(BITTree1,n,r+1,-val);
  
    // Update BIT2
    updateBIT(BITTree2,n,l,val*(l-1));
    updateBIT(BITTree2,n,r+1,-val*r);
}
  
int rangeSum(int l, int r, int BITTree1[], int BITTree2[])
{
    // Find sum from [0,r] then subtract sum
    // from [0,l-1] in order to find sum from
    // [l,r]
    return sum(r, BITTree1, BITTree2) -
           sum(l-1, BITTree1, BITTree2);
}
  
  
int *constructBITree(int n)
{
    // Create and initialize BITree[] as 0
    int *BITree = new int[n+1];
    for (int i=1; i<=n; i++)
        BITree[i] = 0;
  
    return BITree;
}
  
// Driver Program to test above function
int main()
{
    int n = 5;
  
    // Construct two BIT
    int *BITTree1, *BITTree2;
  
    // BIT1 to get element at any index
    // in the array
    BITTree1 = constructBITree(n);
  
    // BIT 2 maintains the extra term
    // which needs to be subtracted
    BITTree2 = constructBITree(n);
  
    // Add 5 to all the elements from [0,4]
    int l = 0 , r = 4 , val = 5;
    updateRange(BITTree1,BITTree2,n,val,l,r);
  
    // Add 2 to all the elements from [2,4]
    l = 2 , r = 4 , val = 10;
    updateRange(BITTree1,BITTree2,n,val,l,r);
  
    // Find sum of all the elements from
    // [1,4]
    l = 1 , r = 4;
    cout << "Sum of elements from [" << l
         << "," << r << "] is ";
    cout << rangeSum(l,r,BITTree1,BITTree2) << "\n";
  
    return 0;
}


Java
// Java program to demonstrate Range Update
// and Range Queries using BIT
import java.util.*;
  
class GFG
{
  
// Returns sum of arr[0..index]. This function assumes
// that the array is preprocessed and partial sums of
// array elements are stored in BITree[]
static int getSum(int BITree[], int index)
{
    int sum = 0; // Initialize result
  
    // index in BITree[] is 1 more than the index in arr[]
    index = index + 1;
  
    // Traverse ancestors of BITree[index]
    while (index > 0)
    {
        // Add current element of BITree to sum
        sum += BITree[index];
  
        // Move index to parent node in getSum View
        index -= index & (-index);
    }
    return sum;
}
  
// Updates a node in Binary Index Tree (BITree) at given
// index in BITree. The given value 'val' is added to
// BITree[i] and all of its ancestors in tree.
static void updateBIT(int BITree[], int n, int index, int val)
{
    // index in BITree[] is 1 more than the index in arr[]
    index = index + 1;
  
    // Traverse all ancestors and add 'val'
    while (index <= n)
    {
        // Add 'val' to current node of BI Tree
        BITree[index] += val;
  
        // Update index to that of parent in update View
        index += index & (-index);
    }
}
  
// Returns the sum of array from [0, x]
static int sum(int x, int BITTree1[], int BITTree2[])
{
    return (getSum(BITTree1, x) * x) - getSum(BITTree2, x);
}
  
  
static void updateRange(int BITTree1[], int BITTree2[], int n,
                int val, int l, int r)
{
    // Update Both the Binary Index Trees
    // As discussed in the article
  
    // Update BIT1
    updateBIT(BITTree1, n, l, val);
    updateBIT(BITTree1, n, r + 1, -val);
  
    // Update BIT2
    updateBIT(BITTree2, n, l, val * (l - 1));
    updateBIT(BITTree2, n, r + 1, -val * r);
}
  
static int rangeSum(int l, int r, int BITTree1[], int BITTree2[])
{
    // Find sum from [0,r] then subtract sum
    // from [0,l-1] in order to find sum from
    // [l,r]
    return sum(r, BITTree1, BITTree2) -
        sum(l - 1, BITTree1, BITTree2);
}
  
  
static int[] constructBITree(int n)
{
    // Create and initialize BITree[] as 0
    int []BITree = new int[n + 1];
    for (int i = 1; i <= n; i++)
        BITree[i] = 0;
  
    return BITree;
}
  
// Driver Program to test above function
public static void main(String[] args)
{
    int n = 5;
  
    // Contwo BIT
    int []BITTree1;
    int []BITTree2;
  
    // BIT1 to get element at any index
    // in the array
    BITTree1 = constructBITree(n);
  
    // BIT 2 maintains the extra term
    // which needs to be subtracted
    BITTree2 = constructBITree(n);
  
    // Add 5 to all the elements from [0,4]
    int l = 0 , r = 4 , val = 5;
    updateRange(BITTree1, BITTree2, n, val, l, r);
  
    // Add 2 to all the elements from [2,4]
    l = 2 ; r = 4 ; val = 10;
    updateRange(BITTree1, BITTree2, n, val, l, r);
  
    // Find sum of all the elements from
    // [1,4]
    l = 1 ; r = 4;
    System.out.print("Sum of elements from [" + l
        + "," + r+ "] is ");
    System.out.print(rangeSum(l, r, BITTree1, BITTree2)+ "\n");
}
}
  
// This code is contributed by 29AjayKumar


Python3
# Python program to demonstrate Range Update
# and Range Queries using BIT
  
# Returns sum of arr[0..index]. This function assumes
# that the array is preprocessed and partial sums of
# array elements are stored in BITree[]
def getSum(BITree: list, index: int) -> int:
    summ = 0 # Initialize result
  
    # index in BITree[] is 1 more than the index in arr[]
    index = index + 1
  
    # Traverse ancestors of BITree[index]
    while index > 0:
  
        # Add current element of BITree to sum
        summ += BITree[index]
  
        # Move index to parent node in getSum View
        index -= index & (-index)
    return summ
  
# Updates a node in Binary Index Tree (BITree) at given
# index in BITree. The given value 'val' is added to
# BITree[i] and all of its ancestors in tree.
def updateBit(BITTree: list, n: int, index: int, val: int) -> None:
  
    # index in BITree[] is 1 more than the index in arr[]
    index = index + 1
  
    # Traverse all ancestors and add 'val'
    while index <= n:
  
        # Add 'val' to current node of BI Tree
        BITTree[index] += val
  
        # Update index to that of parent in update View
        index += index & (-index)
  
  
# Returns the sum of array from [0, x]
def summation(x: int, BITTree1: list, BITTree2: list) -> int:
    return (getSum(BITTree1, x) * x) - getSum(BITTree2, x)
  
  
def updateRange(BITTree1: list, BITTree2: list, n: int, val: int, l: int,
                r: int) -> None:
  
    # Update Both the Binary Index Trees
    # As discussed in the article
  
    # Update BIT1
    updateBit(BITTree1, n, l, val)
    updateBit(BITTree1, n, r + 1, -val)
  
    # Update BIT2
    updateBit(BITTree2, n, l, val * (l - 1))
    updateBit(BITTree2, n, r + 1, -val * r)
  
def rangeSum(l: int, r: int, BITTree1: list, BITTree2: list) -> int:
  
    # Find sum from [0,r] then subtract sum
    # from [0,l-1] in order to find sum from
    # [l,r]
    return summation(r, BITTree1, BITTree2) - summation(
        l - 1, BITTree1, BITTree2)
  
# Driver Code
if __name__ == "__main__":
    n = 5
  
    # BIT1 to get element at any index
    # in the array
    BITTree1 = [0] * (n + 1)
  
    # BIT 2 maintains the extra term
    # which needs to be subtracted
    BITTree2 = [0] * (n + 1)
  
    # Add 5 to all the elements from [0,4]
    l = 0
    r = 4
    val = 5
    updateRange(BITTree1, BITTree2, n, val, l, r)
  
    # Add 2 to all the elements from [2,4]
    l = 2
    r = 4
    val = 10
    updateRange(BITTree1, BITTree2, n, val, l, r)
  
    # Find sum of all the elements from
    # [1,4]
    l = 1
    r = 4
    print("Sum of elements from [%d,%d] is %d" %
        (l, r, rangeSum(l, r, BITTree1, BITTree2)))
  
# This code is contributed by
# sanjeev2552


C#
// C# program to demonstrate Range Update
// and Range Queries using BIT
using System;
  
class GFG
{
  
// Returns sum of arr[0..index]. This function assumes
// that the array is preprocessed and partial sums of
// array elements are stored in BITree[]
static int getSum(int []BITree, int index)
{
    int sum = 0; // Initialize result
  
    // index in BITree[] is 1 more than 
    // the index in []arr
    index = index + 1;
  
    // Traverse ancestors of BITree[index]
    while (index > 0)
    {
        // Add current element of BITree to sum
        sum += BITree[index];
  
        // Move index to parent node in getSum View
        index -= index & (-index);
    }
    return sum;
}
  
// Updates a node in Binary Index Tree (BITree) at given
// index in BITree. The given value 'val' is added to
// BITree[i] and all of its ancestors in tree.
static void updateBIT(int []BITree, int n, 
                      int index, int val)
{
    // index in BITree[] is 1 more than 
    // the index in []arr
    index = index + 1;
  
    // Traverse all ancestors and add 'val'
    while (index <= n)
    {
        // Add 'val' to current node of BI Tree
        BITree[index] += val;
  
        // Update index to that of
        // parent in update View
        index += index & (-index);
    }
}
  
// Returns the sum of array from [0, x]
static int sum(int x, int []BITTree1,
                      int []BITTree2)
{
    return (getSum(BITTree1, x) * x) - 
            getSum(BITTree2, x);
}
  
  
static void updateRange(int []BITTree1, 
                        int []BITTree2, int n,
                        int val, int l, int r)
{
    // Update Both the Binary Index Trees
    // As discussed in the article
  
    // Update BIT1
    updateBIT(BITTree1, n, l, val);
    updateBIT(BITTree1, n, r + 1, -val);
  
    // Update BIT2
    updateBIT(BITTree2, n, l, val * (l - 1));
    updateBIT(BITTree2, n, r + 1, -val * r);
}
  
static int rangeSum(int l, int r, 
                    int []BITTree1, 
                    int []BITTree2)
{
    // Find sum from [0,r] then subtract sum
    // from [0,l-1] in order to find sum from
    // [l,r]
    return sum(r, BITTree1, BITTree2) -
           sum(l - 1, BITTree1, BITTree2);
}
  
static int[] constructBITree(int n)
{
    // Create and initialize BITree[] as 0
    int []BITree = new int[n + 1];
    for (int i = 1; i <= n; i++)
        BITree[i] = 0;
  
    return BITree;
}
  
// Driver Code
public static void Main(String[] args)
{
    int n = 5;
  
    // Contwo BIT
    int []BITTree1;
    int []BITTree2;
  
    // BIT1 to get element at any index
    // in the array
    BITTree1 = constructBITree(n);
  
    // BIT 2 maintains the extra term
    // which needs to be subtracted
    BITTree2 = constructBITree(n);
  
    // Add 5 to all the elements from [0,4]
    int l = 0 , r = 4 , val = 5;
    updateRange(BITTree1, BITTree2, n, val, l, r);
  
    // Add 2 to all the elements from [2,4]
    l = 2 ; r = 4 ; val = 10;
    updateRange(BITTree1, BITTree2, n, val, l, r);
  
    // Find sum of all the elements from
    // [1,4]
    l = 1 ; r = 4;
    Console.Write("Sum of elements from [" + l + 
                             "," + r + "] is ");
    Console.Write(rangeSum(l, r, BITTree1,
                                 BITTree2) + "\n");
}
}
  
// This code is contributed by 29AjayKumar


输出:

Sum of elements from [1,4] is 50

时间复杂度:O(q * log(n)),其中q是查询数。