📜  翻转标志问题延迟传播段树

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

给定大小为N的数组,可以有以下类型的多个查询。

  1. update(l,r) :更新时,翻转(将a [i]乘以-1)a [i]的值,其中l <= i <= r。简单来说,将a [i]的符号更改为给定范围。
  2. query(l,r) :查询时,打印给定范围(包括l和r)中数组的总和。

例子 :

先决条件:

  1. 段树
  2. 分段树中的延迟传播

方法 :
创建一个分段树,每个节点都将存储其左,右子节点的和,直到它是存储该数组的叶节点为止。

对于更新操作:
创建一个名为lazy的树,其大小与上述分段树的大小相同,其中树存储其子项和lazy的总和,无论是否要求翻转它们。如果将某个范围的惰性设置为1,则需要翻转该范围内的所有值。为了进行更新,使用了以下操作–

  • 如果当前段树的lazy设置为1,则通过更改符号来更新当前段树节点,因为需要翻转其值,并且还翻转其子级的lazy的值并将其自己的lazy重置为0。
  • 如果当前节点的范围完全在更新查询范围之内,则通过更改其符号来更新当前节点,如果不是叶节点,还可以翻转其子节点的lazy值。
  • 如果当前节点的范围与更新范围重叠,则对其子节点进行递归,并使用其子节点的总和来更新当前节点。

对于查询操作:
如果lazy设置为1,则更改当前节点的符号,并将lazy当前节点重置为0,如果不是叶节点,则翻转其子节点的lazy值。然后像段树一样进行简单查询。

下面是上述方法的实现:

C++
// C++ implementation of the approach
#include 
using namespace std;
  
#define MAX 15
   
int tree[MAX] = { 0 };
int lazy[MAX] = { 0 };
   
// Function to build the segment tree
void build(int arr[],int node, int a, int b)
{
    if (a == b) {
        tree[node] = arr[a];
        return;
    }
   
    // left child
    build(arr,2 * node, a, (a + b) / 2); 
  
    // right child
    build(arr,2 * node + 1, 1 + (a + b) / 2, b); 
   
    tree[node] = tree[node * 2] + 
                         tree[node * 2 + 1];
}
   
void update_tree(int node, int a, 
                int b, int i, int j)
{
   
    // If lazy of node is 1 then 
    // value in current node 
    // needs to be flipped
    if (lazy[node] != 0) {
  
        // Update it
        tree[node] = tree[node] * (-1); 
   
        if (a != b) {
  
            // flip value in lazy
            lazy[node * 2] =
                        !(lazy[node * 2]); 
  
             // flip value in lazy
            lazy[node * 2 + 1] = 
                    !(lazy[node * 2 + 1]);
        }
    
        // Reset the node lazy value
        lazy[node] = 0; 
    }
   
    // Current segment is not
    // within range [i, j]
    if (a > b || a > j || b < i)
        return;
   
    // Segment is fully
    // within range
    if (a >= i && b <= j) {
        tree[node] = tree[node] * (-1);
   
        // Not leaf node
        if (a != b) {
  
            // Flip the value as if lazy is 
            // 1 and again asked to flip 
            // value then without flipping 
            // value two times
            lazy[node * 2] = 
                         !(lazy[node * 2]);
            lazy[node * 2 + 1] = 
                    !(lazy[node * 2 + 1]);
        }
   
        return;
    }
   
    // Updating left child
    update_tree(node * 2, a,
                        (a + b) / 2, i, j);
  
    // Updating right child
    update_tree(1 + node * 2, 1 + 
                     (a + b) / 2, b, i, j); 
  
    // Updating root with 
    // sum of its child
    tree[node] = tree[node * 2] +
                     tree[node * 2 + 1];
}
   
int query_tree(int node, int a, 
                      int b, int i, int j)
{
    // Out of range 
    if (a > b || a > j || b < i)
        return 0; 
   
    // If lazy of node is 1 then value
    // in current node needs to be flipped
    if (lazy[node] != 0) {
  
     
        tree[node] = tree[node] * (-1); 
        if (a != b) {
            lazy[node * 2] = 
                        !(lazy[node * 2]); 
  
            // flip value in lazy
            lazy[node * 2 + 1] = 
                    !(lazy[node * 2 + 1]); 
        }
   
        lazy[node] = 0;
    }
   
    // Current segment is totally 
    // within range [i, j]
    if (a >= i && b <= j)
        return tree[node];
    
    // Query left child
    int q1 = query_tree(node * 2, a,
                        (a + b) / 2, i, j); 
  
    // Query right child
    int q2 = query_tree(1 + node * 2, 
                 1 + (a + b) / 2, b, i, j); 
   
    // Return final result
    return q1 + q2;
}
   
int main()
{
  
    int arr[]={1,2,3,4,5};
  
    int n=sizeof(arr)/sizeof(arr[0]);
  
    // Building segment tree
    build(arr,1, 0, n - 1);
  
    // Array is { 1, 2, 3, 4, 5 }
    cout << query_tree(1, 0, n - 1, 0, 4) << endl;
   
    // Flip range 0 to 4
    update_tree(1, 0, n - 1, 0, 4);
  
    // Array becomes { -1, -2, -3, -4, -5 }
    cout << query_tree(1, 0, n - 1, 0, 4) << endl;
   
    // Flip range 0 t0 2
    update_tree(1, 0, n - 1, 0, 2);
  
    // Array becomes { 1, 2, 3, -4, -5 }
    cout << query_tree(1, 0, n - 1, 0, 4) << endl;
  
}


Java
// Java implementation of the approach
class GFG
{
  
static final int MAX = 15;
  
static int tree[] = new int[MAX];
static boolean lazy[] = new boolean[MAX];
  
// Function to build the segment tree
static void build(int arr[],int node, int a, int b)
{
    if (a == b)
    {
        tree[node] = arr[a];
        return;
    }
  
    // left child
    build(arr,2 * node, a, (a + b) / 2); 
  
    // right child
    build(arr,2 * node + 1, 1 + (a + b) / 2, b); 
  
    tree[node] = tree[node * 2] + 
                        tree[node * 2 + 1];
}
  
static void update_tree(int node, int a, 
                int b, int i, int j)
{
  
    // If lazy of node is 1 then 
    // value in current node 
    // needs to be flipped
    if (lazy[node] != false) 
    {
  
        // Update it
        tree[node] = tree[node] * (-1); 
  
        if (a != b)
        {
  
            // flip value in lazy
            lazy[node * 2] =
                        !(lazy[node * 2]); 
  
            // flip value in lazy
            lazy[node * 2 + 1] = 
                    !(lazy[node * 2 + 1]);
        }
      
        // Reset the node lazy value
        lazy[node] = false; 
    }
  
    // Current segment is not
    // within range [i, j]
    if (a > b || a > j || b < i)
        return;
  
    // Segment is fully
    // within range
    if (a >= i && b <= j) 
    {
        tree[node] = tree[node] * (-1);
  
        // Not leaf node
        if (a != b) 
        {
  
            // Flip the value as if lazy is 
            // 1 and again asked to flip 
            // value then without flipping 
            // value two times
            lazy[node * 2] = 
                        !(lazy[node * 2]);
            lazy[node * 2 + 1] = 
                    !(lazy[node * 2 + 1]);
        }
  
        return;
    }
  
    // Updating left child
    update_tree(node * 2, a,
                        (a + b) / 2, i, j);
  
    // Updating right child
    update_tree(1 + node * 2, 1 + 
                    (a + b) / 2, b, i, j); 
  
    // Updating root with 
    // sum of its child
    tree[node] = tree[node * 2] +
                    tree[node * 2 + 1];
}
  
static int query_tree(int node, int a, 
                    int b, int i, int j)
{
    // Out of range 
    if (a > b || a > j || b < i)
        return 0;
  
    // If lazy of node is 1 then value
    // in current node needs to be flipped
    if (lazy[node] != false) 
    {
  
      
        tree[node] = tree[node] * (-1); 
        if (a != b)
        {
            lazy[node * 2] = 
                        !(lazy[node * 2]); 
  
            // flip value in lazy
            lazy[node * 2 + 1] = 
                    !(lazy[node * 2 + 1]); 
        }
  
        lazy[node] = false;
    }
  
    // Current segment is totally 
    // within range [i, j]
    if (a >= i && b <= j)
        return tree[node];
      
    // Query left child
    int q1 = query_tree(node * 2, a,
                        (a + b) / 2, i, j); 
  
    // Query right child
    int q2 = query_tree(1 + node * 2, 
                1 + (a + b) / 2, b, i, j); 
  
    // Return final result
    return q1 + q2;
}
  
// Driver code
public static void main(String[] args)
{
  
    int arr[]={1, 2, 3, 4, 5};
  
    int n=arr.length;
  
    // Building segment tree
    build(arr,1, 0, n - 1);
  
    // Array is { 1, 2, 3, 4, 5 }
    System.out.print(query_tree(1, 0, n - 1, 0, 4) +"\n");
  
    // Flip range 0 to 4
    update_tree(1, 0, n - 1, 0, 4);
  
    // Array becomes { -1, -2, -3, -4, -5 }
    System.out.print(query_tree(1, 0, n - 1, 0, 4) +"\n");
  
    // Flip range 0 t0 2
    update_tree(1, 0, n - 1, 0, 2);
  
    // Array becomes { 1, 2, 3, -4, -5 }
    System.out.print(query_tree(1, 0, n - 1, 0, 4) +"\n");
}
}
  
// This code contributed by Rajput-Ji


Python3
# Python3 implementation of the approach
MAX = 15
  
tree = [0]*MAX
lazy = [0]*MAX
  
# Function to build the segment tree
def build(arr,node, a, b):
    if (a == b):
        tree[node] = arr[a]
        return
  
    # left child
    build(arr,2 * node, a, (a + b) // 2)
  
    # right child
    build(arr,2 * node + 1, 1 + (a + b) // 2, b)
  
    tree[node] = tree[node * 2] +tree[node * 2 + 1]
  
def update_tree(node, a,b, i, j):
  
    # If lazy of node is 1 then
    # value in current node
    # needs to be flipped
    if (lazy[node] != 0):
  
        # Update it
        tree[node] = tree[node] * (-1)
  
        if (a != b):
  
            # flip value in lazy
            lazy[node * 2] =not (lazy[node * 2])
  
            # flip value in lazy
            lazy[node * 2 + 1] =not (lazy[node * 2 + 1])
  
  
        # Reset the node lazy value
        lazy[node] = 0
  
  
    # Current segment is not
    # within range [i, j]
    if (a > b or a > j or b < i):
        return
  
    # Segment is fully
    # within range
    if (a >= i and b <= j):
        tree[node] = tree[node] * (-1)
  
        # Not leaf node
        if (a != b):
  
            # Flip the value as if lazy is
            # 1 and again asked to flip
            # value then without flipping
            # value two times
            lazy[node * 2] = not (lazy[node * 2])
            lazy[node * 2 + 1] = not (lazy[node * 2 + 1])
  
  
        return
  
  
    # Updating left child
    update_tree(node * 2, a,(a + b) // 2, i, j)
  
    # Updating right child
    update_tree(1 + node * 2, 1 +(a + b) // 2, b, i, j)
  
    # Updating root with
    # sum of its child
    tree[node] = tree[node * 2] +tree[node * 2 + 1]
  
  
def query_tree(node, a,b, i, j):
    # Out of range
    if (a > b or a > j or b < i):
        return 0
  
    # If lazy of node is 1 then value
    # in current node needs to be flipped
    if (lazy[node] != 0):
  
  
        tree[node] = tree[node] * (-1)
        if (a != b):
            lazy[node * 2] =not (lazy[node * 2])
  
            # flip value in lazy
            lazy[node * 2 + 1] = not (lazy[node * 2 + 1])
  
  
        lazy[node] = 0
  
  
    # Current segment is totally
    # within range [i, j]
    if (a >= i and b <= j):
        return tree[node]
  
    # Query left child
    q1 = query_tree(node * 2, a,(a + b) // 2, i, j)
  
    # Query right child
    q2 = query_tree(1 + node * 2,1 + (a + b) // 2, b, i, j)
  
    # Return final result
    return q1 + q2
  
# Driver code
if __name__ == '__main__':
  
    arr=[1,2,3,4,5]
  
    n=len(arr)
  
    # Building segment tree
    build(arr,1, 0, n - 1)
  
    # Array is { 1, 2, 3, 4, 5
    print(query_tree(1, 0, n - 1, 0, 4))
  
    # Flip range 0 to 4
    update_tree(1, 0, n - 1, 0, 4)
  
    # Array becomes { -1, -2, -3, -4, -5
    print(query_tree(1, 0, n - 1, 0, 4))
  
    # Flip range 0 t0 2
    update_tree(1, 0, n - 1, 0, 2)
  
    # Array becomes { 1, 2, 3, -4, -5
    print(query_tree(1, 0, n - 1, 0, 4))
  
# This code is contributed by mohit kumar 29


C#
// C# implementation of the approach
using System;
  
class GFG
{
  
static readonly int MAX = 15;
  
static int []tree = new int[MAX];
static bool []lazy = new bool[MAX];
  
// Function to build the segment tree
static void build(int []arr,int node, int a, int b)
{
    if (a == b)
    {
        tree[node] = arr[a];
        return;
    }
  
    // left child
    build(arr, 2 * node, a, (a + b) / 2); 
  
    // right child
    build(arr, 2 * node + 1, 1 + (a + b) / 2, b); 
  
    tree[node] = tree[node * 2] + 
                        tree[node * 2 + 1];
}
  
static void update_tree(int node, int a, 
                int b, int i, int j)
{
  
    // If lazy of node is 1 then 
    // value in current node 
    // needs to be flipped
    if (lazy[node] != false) 
    {
  
        // Update it
        tree[node] = tree[node] * (-1); 
  
        if (a != b)
        {
  
            // flip value in lazy
            lazy[node * 2] =
                        !(lazy[node * 2]); 
  
            // flip value in lazy
            lazy[node * 2 + 1] = 
                    !(lazy[node * 2 + 1]);
        }
      
        // Reset the node lazy value
        lazy[node] = false; 
    }
  
    // Current segment is not
    // within range [i, j]
    if (a > b || a > j || b < i)
        return;
  
    // Segment is fully
    // within range
    if (a >= i && b <= j) 
    {
        tree[node] = tree[node] * (-1);
  
        // Not leaf node
        if (a != b) 
        {
  
            // Flip the value as if lazy is 
            // 1 and again asked to flip 
            // value then without flipping 
            // value two times
            lazy[node * 2] = 
                        !(lazy[node * 2]);
            lazy[node * 2 + 1] = 
                    !(lazy[node * 2 + 1]);
        }
  
        return;
    }
  
    // Updating left child
    update_tree(node * 2, a,
                        (a + b) / 2, i, j);
  
    // Updating right child
    update_tree(1 + node * 2, 1 + 
                    (a + b) / 2, b, i, j); 
  
    // Updating root with 
    // sum of its child
    tree[node] = tree[node * 2] +
                    tree[node * 2 + 1];
}
  
static int query_tree(int node, int a, 
                    int b, int i, int j)
{
    // Out of range 
    if (a > b || a > j || b < i)
        return 0;
  
    // If lazy of node is 1 then value
    // in current node needs to be flipped
    if (lazy[node] != false) 
    {
        tree[node] = tree[node] * (-1); 
        if (a != b)
        {
            lazy[node * 2] = 
                        !(lazy[node * 2]); 
  
            // flip value in lazy
            lazy[node * 2 + 1] = 
                    !(lazy[node * 2 + 1]); 
        }
  
        lazy[node] = false;
    }
  
    // Current segment is totally 
    // within range [i, j]
    if (a >= i && b <= j)
        return tree[node];
      
    // Query left child
    int q1 = query_tree(node * 2, a,
                        (a + b) / 2, i, j); 
  
    // Query right child
    int q2 = query_tree(1 + node * 2, 
                1 + (a + b) / 2, b, i, j); 
  
    // Return readonly result
    return q1 + q2;
}
  
// Driver code
public static void Main(String[] args)
{
  
    int []arr = {1, 2, 3, 4, 5};
  
    int n = arr.Length;
  
    // Building segment tree
    build(arr, 1, 0, n - 1);
  
    // Array is { 1, 2, 3, 4, 5 }
    Console.Write(query_tree(1, 0, n - 1, 0, 4) +"\n");
  
    // Flip range 0 to 4
    update_tree(1, 0, n - 1, 0, 4);
  
    // Array becomes { -1, -2, -3, -4, -5 }
    Console.Write(query_tree(1, 0, n - 1, 0, 4) +"\n");
  
    // Flip range 0 t0 2
    update_tree(1, 0, n - 1, 0, 2);
  
    // Array becomes { 1, 2, 3, -4, -5 }
    Console.Write(query_tree(1, 0, n - 1, 0, 4) +"\n");
}
}
  
// This code is contributed by Rajput-Ji


输出:
15
-15
-3

时间复杂度: O(log(N))