Árboles de segmentos dinámicos: consultas en línea para la suma de rangos con actualizaciones de puntos

Prerrequisitos: Árbol de segmentos
Dado un número N que representa el tamaño de la array inicializada en 0 y Q consultas para procesar donde hay dos tipos de consultas: 

  1. 1 PV: Ponga el valor V en la posición P .
  2. 2 LR: salida de la suma de valores de L a R .

La tarea es responder a estas consultas. 

Restricciones:  

  • 1 ≤ norte ≤ 10 18
  • Q ≤ 10 5
  • 1 ≤ L ≤ R≤ norte

Nota: Las consultas son en línea. Por lo tanto:  

  • L = (respuesta anterior + L) % N + 1
  • R = (respuesta anterior + R) % N + 1

Ejemplos: 

Entrada: N = 5, Q = 5, arr[][] = {{1, 2, 3}, {1, 1, 4}, {1, 3, 5}, {1, 4, 7}, { 2, 3, 4}} 
Salida: 12 
Explicación: 
Hay cinco consultas. Dado que N = 5, por lo tanto, inicialmente, la array es {0, 0, 0, 0, 0} 
Para la consulta 1: 1 2 3 array = {0, 3, 0, 0, 0} 
Para la consulta 2: 1 1 4 array = {4, 3, 0, 0, 0} 
Para la consulta 3: 1 3 5 array = {4, 3, 5, 0, 0} 
Para la consulta 4: 1 4 7 array = {4, 3, 5, 7 , 0} 
Para la consulta 5: 2 3 4 Suma de [3, 4] = 7 + 5 = 12.

Entrada: N = 3, Q = 2, arr[][] = {{1, 1, 1}, {1, 2, 2}, {1, 3, 3}} 
Salida: 0  

Enfoque: aquí, dado que las actualizaciones son altas, el algoritmo de Kadane no funciona del todo bien. Además, dado que las consultas son en línea, un simple árbol de segmentos no podría resolver este problema porque las restricciones por el número de elementos son muy altas. Por lo tanto, en este problema se utiliza un nuevo tipo de estructura de datos, un árbol de segmento dinámico. 
Árbol de segmentos dinámicos: el árbol de segmentos dinámicos no es una nueva estructura de datos. Es muy similar al árbol de segmentos . Las siguientes son las propiedades del árbol de segmentos dinámicos: 

  • En lugar de usar una array para representar los intervalos, se crea un Node cada vez que se actualiza un nuevo intervalo.
  • La siguiente es la estructura del Node del árbol de segmentos dinámicos:

C++

// Every node contains the value and 
// the left subtree and right subtree 
struct Node { 
    long long value; 
    struct Node *L, *R; 
}; 
    
struct Node* getnode() 
{ 
    struct Node* temp = new struct Node; 
    temp->value = 0; 
    temp->L = NULL; 
    temp->R = NULL; 
    return temp; 
}
  • Claramente, la estructura anterior es la misma que un árbol de búsqueda binaria . En cada Node, estamos almacenando el valor del Node y dos punteros que apuntan al subárbol izquierdo y derecho.
  • El Intervalo de la raíz es desde [1, N], el intervalo del subárbol izquierdo será [1, N/2] y el intervalo del subárbol derecho será [N/2 + 1, N].
  • De manera similar, para cada Node, podemos calcular el intervalo que representa. Digamos que el intervalo del Node actual es [L, R]. Entonces, el Intervalo de su subárbol izquierdo y derecho son [L, (L + R)/2] y [(L + R)/2+1, R] respectivamente.
  • Dado que estamos creando un nuevo Node solo cuando es necesario, la función build() del árbol de segmentos se elimina por completo.

Antes de entrar en el algoritmo de las operaciones, definamos los términos utilizados en este artículo:  

  • Intervalo del Node: Es el intervalo que representa el Node.
  • Intervalo requerido: Intervalo para el cual se va a calcular la suma.
  • Índice requerido: índice en el que se requiere actualización.

El siguiente es el algoritmo utilizado para las operaciones sobre el árbol con las propiedades antes mencionadas: 

  1. Actualización de puntos: El siguiente algoritmo se utiliza para la actualización de puntos: 
    1. Comience con el Node raíz.
    2. Si el intervalo en el Node no se superpone con el índice requerido, regrese.
    3. Si el Node es una entrada NULL, cree un nuevo Node con los intervalos apropiados y descienda a ese Node volviendo al paso 2 para cada nuevo hijo creado.
    4. Si tanto los intervalos como el índice en el que se va a almacenar el valor son iguales, almacene el valor en ese Node.
    5. Si el intervalo en el Node se superpone parcialmente con el índice requerido, descienda a sus hijos y continúe la ejecución desde el paso 2.
  2. Encontrar la suma para cada consulta: El siguiente algoritmo se utiliza para encontrar la suma para cada consulta: 
    1. Comience con el Node raíz.
    2. Si el Node es NULL o el intervalo en ese Node no se superpone con el intervalo requerido, devuelva 0.
    3. Si el intervalo en el Node se superpone completamente con el intervalo requerido, devuelva el valor almacenado en el Node.
    4. Si el intervalo en el Node se superpone parcialmente con el intervalo requerido, descienda a sus hijos y continúe la ejecución desde el paso 2 para ambos hijos.

Ejemplo: Permite visualizar la actualización y la suma con un ejemplo. Sea N = 10 y las operaciones necesarias para realizar en el árbol son las siguientes: 

  1. Inserte 10 en la posición 1.
  2. Encuentra la suma de los valores de los índices de 2 a 8.
  3. Inserte 3 en la posición 5.
  4. Encuentra la suma de los valores de los índices de 3 a 6.
  • Inicialmente, para el valor N = 10, el árbol está vacío. Por lo tanto: 
     

  • Inserte 10 en la posición 1. Para hacer esto, cree un nuevo Node hasta que obtengamos el intervalo requerido. Por lo tanto: 
     

  • Encuentre la suma del valor de los índices de 2 a 8. Para hacer esto, se encuentra la suma de [1, 8] y se le resta el valor [1, 2]. Dado que el Node [1, 8] aún no se ha creado, el valor de [1, 8] es el valor de la raíz [1, 10]. Por lo tanto: 
     

  • Inserte 3 en la posición 5. Para hacer esto, cree un nuevo Node hasta que obtengamos el intervalo requerido. Por lo tanto: 
     

A continuación se muestra la implementación del enfoque anterior:

C++

// C++ program for the implementation
// of the Dynamic segment tree and
// perform the range updates on the
// given queries
  
#include <bits/stdc++.h>
  
using namespace std;
typedef long long ll;
  
// Structure of the node
struct Node {
  
    ll value;
    struct Node *L, *R;
};
  
// Structure to get the newly formed
// node
struct Node* getnode()
{
    struct Node* temp = new struct Node;
    temp->value = 0;
    temp->L = NULL;
    temp->R = NULL;
    return temp;
}
  
// Creating the Root node
struct Node* root;
  
// Function to perform the point update
// on the dynamic segment tree
void UpdateHelper(struct Node* curr, ll index,
                  ll L, ll R, ll val)
{
  
    // If the index is not overlapping
    // with the index
    if (L > index || R < index)
        return;
  
    // If the index is completely overlapping
    // with the index
    if (L == R && L == index) {
  
        // Update the value of the node
        // to the given value
        curr->value = val;
        return;
    }
  
    // Computing the middle index if none
    // of the above base cases are satisfied
    ll mid = L - (L - R) / 2;
    ll sum1 = 0, sum2 = 0;
  
    // If the index is in the left subtree
    if (index <= mid) {
  
        // Create a new node if the left
        // subtree is is null
        if (curr->L == NULL)
            curr->L = getnode();
  
        // Recursively call the function
        // for the left subtree
        UpdateHelper(curr->L, index, L, mid, val);
    }
  
    // If the index is in the right subtree
    else {
  
        // Create a new node if the right
        // subtree is is null
        if (curr->R == NULL)
            curr->R = getnode();
  
        // Recursively call the function
        // for the right subtree
        UpdateHelper(curr->R, index, mid + 1, R, val);
    }
  
    // Storing the sum of the left subtree
    if (curr->L)
        sum1 = curr->L->value;
  
    // Storing the sum of the right subtree
    if (curr->R)
        sum2 = curr->R->value;
  
    // Storing the sum of the children into
    // the node's value
    curr->value = sum1 + sum2;
    return;
}
  
// Function to find the sum of the
// values given by the range
ll queryHelper(struct Node* curr, ll a,
               ll b, ll L, ll R)
{
  
    // Return 0 if the root is null
    if (curr == NULL)
        return 0;
  
    // If the index is not overlapping
    // with the index, then the node
    // is not created. So sum is 0
    if (L > b || R < a)
        return 0;
  
    // If the index is completely overlapping
    // with the index, return the node's value
    if (L >= a && R <= b)
        return curr->value;
  
    ll mid = L - (L - R) / 2;
  
    // Return the sum of values stored
    // at the node's children
    return queryHelper(curr->L, a, b, L, mid)
           + queryHelper(curr->R, a, b, mid + 1, R);
}
  
// Function to call the queryHelper
// function to find the sum for
// the query
ll query(int L, int R)
{
    return queryHelper(root, L, R, 1, 10);
}
  
// Function to call the UpdateHelper
// function for the point update
void update(int index, int value)
{
    UpdateHelper(root, index, 1, 10, value);
}
  
// Function to perform the operations
// on the tree
void operations()
{
    // Creating an empty tree
    root = getnode();
  
    // Update the value at position 1 to 10
    update(1, 10);
  
    // Update the value at position 3 to 5
    update(3, 5);
  
    // Finding sum for the range [2, 8]
    cout << query(2, 8) << endl;
  
    // Finding sum for the range [1, 10]
    cout << query(1, 10) << endl;
  
}
  
// Driver code
int main()
{
    operations();
  
    return 0;
}

Java

// Java program for the implementation
// of the Dynamic segment tree and
// perform the range updates on the
// given queries
  
class GFG {
  
    // Structure of the node
    static class Node {
        int value;
        Node L, R;
  
    }
  
    // Structure to get the newly formed
    // node
    static Node getnode() {
        Node temp = new Node();
        temp.value = 0;
        temp.L = null;
        temp.R = null;
        return temp;
    }
  
    // Creating the Root node
    static Node root = new Node();
  
    // Function to perform the point update
    // on the dynamic segment tree
    static void UpdateHelper(Node curr, int index, int L, int R, int val) {
  
        // If the index is not overlapping
        // with the index
        if (L > index || R < index)
            return;
  
        // If the index is completely overlapping
        // with the index
        if (L == R && L == index) {
  
            // Update the value of the node
            // to the given value
            curr.value = val;
            return;
        }
  
        // Computing the middle index if none
        // of the above base cases are satisfied
        int mid = L - (L - R) / 2;
        int sum1 = 0, sum2 = 0;
  
        // If the index is in the left subtree
        if (index <= mid) {
  
            // Create a new node if the left
            // subtree is is null
            if (curr.L == null)
                curr.L = getnode();
  
            // Recursively call the function
            // for the left subtree
            UpdateHelper(curr.L, index, L, mid, val);
        }
  
        // If the index is in the right subtree
        else {
  
            // Create a new node if the right
            // subtree is is null
            if (curr.R == null)
                curr.R = getnode();
  
            // Recursively call the function
            // for the right subtree
            UpdateHelper(curr.R, index, mid + 1, R, val);
        }
  
        // Storing the sum of the left subtree
        if (curr.L != null)
            sum1 = curr.L.value;
  
        // Storing the sum of the right subtree
        if (curr.R != null)
            sum2 = curr.R.value;
  
        // Storing the sum of the children into
        // the node's value
        curr.value = sum1 + sum2;
        return;
    }
  
    // Function to find the sum of the
    // values given by the range
    static int queryHelper(Node curr, int a, int b, int L, int R) {
  
        // Return 0 if the root is null
        if (curr == null)
            return 0;
  
        // If the index is not overlapping
        // with the index, then the node
        // is not created. So sum is 0
        if (L > b || R < a)
            return 0;
  
        // If the index is completely overlapping
        // with the index, return the node's value
        if (L >= a && R <= b)
            return curr.value;
  
        int mid = L - (L - R) / 2;
  
        // Return the sum of values stored
        // at the node's children
        return queryHelper(curr.L, a, b, L, mid) + queryHelper(curr.R, a, b, mid + 1, R);
    }
  
    // Function to call the queryHelper
    // function to find the sum for
    // the query
    static int query(int L, int R) {
        return queryHelper(root, L, R, 1, 10);
    }
  
    // Function to call the UpdateHelper
    // function for the point update
    static void update(int index, int value) {
        UpdateHelper(root, index, 1, 10, value);
    }
  
    // Function to perform the operations
    // on the tree
    static void operations() {
        // Creating an empty tree
        root = getnode();
  
        // Update the value at position 1 to 10
        update(1, 10);
  
        // Update the value at position 3 to 5
        update(3, 5);
  
        // Finding sum for the range [2, 8]
        System.out.println(query(2, 8));
  
        // Finding sum for the range [1, 10]
        System.out.println(query(1, 10));
  
    }
  
    // Driver code
    public static void main(String[] args) {
        operations();
    }
}
  
// This code is contributed by sanjeev2552

Python3

# C++ program for the implementation
# of the Dynamic segment tree and
# perform the range updates on the
# given queries
  
  
# Structure of the node
class Node:
    def __init__(self):
        self.value=-1
        self.L, self.R=None,None
  
  
# Structure to get the newly formed
# node
def getnode():
    temp = Node()
    temp.value = 0
    temp.L = None
    temp.R = None
    return temp
  
  
# Creating the Root node
root=None
  
# Function to perform the point update
# on the dynamic segment tree
def UpdateHelper(curr, index, L, R, val):
  
    # If the index is not overlapping
    # with the index
    if (L > index or R < index):
        return
  
    # If the index is completely overlapping
    # with the index
    if (L == R and L == index) :
  
        # Update the value of the node
        # to the given value
        curr.value = val
        return
      
  
    # Computing the middle index if none
    # of the above base cases are satisfied
    mid = int(L - (L - R) / 2)
    sum1 = 0; sum2 = 0
  
    # If the index is in the left subtree
    if (index <= mid) :
  
        # Create a new node if the left
        # subtree is is None
        if (curr.L == None):
            curr.L = getnode()
  
        # Recursively call the function
        # for the left subtree
        UpdateHelper(curr.L, index, L, mid, val)
      
  
    # If the index is in the right subtree
    else :
  
        # Create a new node if the right
        # subtree is is None
        if (curr.R == None):
            curr.R = getnode()
  
        # Recursively call the function
        # for the right subtree
        UpdateHelper(curr.R, index, mid + 1, R, val)
      
  
    # Storing the sum of the left subtree
    if (curr.L):
        sum1 = curr.L.value
  
    # Storing the sum of the right subtree
    if (curr.R):
        sum2 = curr.R.value
  
    # Storing the sum of the children into
    # the node's value
    curr.value = sum1 + sum2
    return
  
  
# Function to find the sum of the
# values given by the range
def queryHelper(curr, a, b, L, R):
  
    # Return 0 if the root is None
    if (curr == None):
        return 0
  
    # If the index is not overlapping
    # with the index, then the node
    # is not created. So sum is 0
    if (L > b or R < a):
        return 0
  
    # If the index is completely overlapping
    # with the index, return the node's value
    if (L >= a and R <= b):
        return curr.value
  
    mid = int(L - (L - R) / 2)
  
    # Return the sum of values stored
    # at the node's children
    return queryHelper(curr.L, a, b, L, mid) + queryHelper(curr.R, a, b, mid + 1, R)
  
  
# Function to call the queryHelper
# function to find the sum for
# the query
def query(L, R):
    return queryHelper(root, L, R, 1, 10)
  
  
# Function to call the UpdateHelper
# function for the point update
def update(index, value):
    UpdateHelper(root, index, 1, 10, value)
  
  
# Function to perform the operations
# on the tree
def operations():
    global root
    # Creating an empty tree
    root = getnode()
  
    # Update the value at position 1 to 10
    update(1, 10)
  
    # Update the value at position 3 to 5
    update(3, 5)
  
    # Finding sum for the range [2, 8]
    print(query(2, 8))
  
    # Finding sum for the range [1, 10]
    print(query(1, 10))
  
  
  
# Driver code
if __name__ == '__main__':
    operations()

C#

using System;
  
// C# program for the implementation
// of the Dynamic segment tree and
// perform the range updates on the
// given queries
  
class GFG {
  
  // Structure of the node
  public  class Node {
    public int value;
    public Node L, R;
  
  }
  
  // Structure to get the newly formed
  // node
  static Node getnode() {
    Node temp = new Node();
    temp.value = 0;
    temp.L = null;
    temp.R = null;
    return temp;
  }
  
  // Creating the Root node
  static Node root = new Node();
  
  // Function to perform the point update
  // on the dynamic segment tree
  static void UpdateHelper(Node curr, int index, int L, int R, int val) {
  
    // If the index is not overlapping
    // with the index
    if (L > index || R < index)
      return;
  
    // If the index is completely overlapping
    // with the index
    if (L == R && L == index) {
  
      // Update the value of the node
      // to the given value
      curr.value = val;
      return;
    }
  
    // Computing the middle index if none
    // of the above base cases are satisfied
    int mid = L - (L - R) / 2;
    int sum1 = 0, sum2 = 0;
  
    // If the index is in the left subtree
    if (index <= mid) {
  
      // Create a new node if the left
      // subtree is is null
      if (curr.L == null)
        curr.L = getnode();
  
      // Recursively call the function
      // for the left subtree
      UpdateHelper(curr.L, index, L, mid, val);
    }
  
    // If the index is in the right subtree
    else {
  
      // Create a new node if the right
      // subtree is is null
      if (curr.R == null)
        curr.R = getnode();
  
      // Recursively call the function
      // for the right subtree
      UpdateHelper(curr.R, index, mid + 1, R, val);
    }
  
    // Storing the sum of the left subtree
    if (curr.L != null)
      sum1 = curr.L.value;
  
    // Storing the sum of the right subtree
    if (curr.R != null)
      sum2 = curr.R.value;
  
    // Storing the sum of the children into
    // the node's value
    curr.value = sum1 + sum2;
    return;
  }
  
  // Function to find the sum of the
  // values given by the range
  static int queryHelper(Node curr, int a, int b, int L, int R) {
  
    // Return 0 if the root is null
    if (curr == null)
      return 0;
  
    // If the index is not overlapping
    // with the index, then the node
    // is not created. So sum is 0
    if (L > b || R < a)
      return 0;
  
    // If the index is completely overlapping
    // with the index, return the node's value
    if (L >= a && R <= b)
      return curr.value;
  
    int mid = L - (L - R) / 2;
  
    // Return the sum of values stored
    // at the node's children
    return queryHelper(curr.L, a, b, L, mid) + queryHelper(curr.R, a, b, mid + 1, R);
  }
  
  // Function to call the queryHelper
  // function to find the sum for
  // the query
  static int query(int L, int R) {
    return queryHelper(root, L, R, 1, 10);
  }
  
  // Function to call the UpdateHelper
  // function for the point update
  static void update(int index, int value) {
    UpdateHelper(root, index, 1, 10, value);
  }
  
  // Function to perform the operations
  // on the tree
  static void operations() {
    // Creating an empty tree
    root = getnode();
  
    // Update the value at position 1 to 10
    update(1, 10);
  
    // Update the value at position 3 to 5
    update(3, 5);
  
    // Finding sum for the range [2, 8]
    Console.WriteLine(query(2, 8));
  
    // Finding sum for the range [1, 10]
    Console.WriteLine(query(1, 10));
  
  }
  
  // Driver code
  public static void Main(String[] args) {
    operations();
  }
}
  
// This code is contributed by jana_sayantan.

Javascript

<script>
  
// Javascript program for the implementation
// of the Dynamic segment tree and perform 
// the range updates on the given queries
  
// Structure of the node
class Node
{
    constructor()
    {
        this.L = null;
        this.R = null;
        this.value = 0;
    }
}
  
// Structure to get the newly formed
// node
function getnode() 
{
    let temp = new Node();
    return temp;
}
  
// Creating the Root node
let root = new Node();
  
// Function to perform the point update
// on the dynamic segment tree
function UpdateHelper(curr, index, L, R, val)
{
      
    // If the index is not overlapping
    // with the index
    if (L > index || R < index)
        return;
  
    // If the index is completely overlapping
    // with the index
    if (L == R && L == index)
    {
          
        // Update the value of the node
        // to the given value
        curr.value = val;
        return;
    }
  
    // Computing the middle index if none
    // of the above base cases are satisfied
    let mid = L - parseInt((L - R) / 2, 10);
    let sum1 = 0, sum2 = 0;
  
    // If the index is in the left subtree
    if (index <= mid)
    {
          
        // Create a new node if the left
        // subtree is is null
        if (curr.L == null)
            curr.L = getnode();
  
        // Recursively call the function
        // for the left subtree
        UpdateHelper(curr.L, index, L, mid, val);
    }
  
    // If the index is in the right subtree
    else
    {
          
        // Create a new node if the right
        // subtree is is null
        if (curr.R == null)
            curr.R = getnode();
  
        // Recursively call the function
        // for the right subtree
        UpdateHelper(curr.R, index, mid + 1, R, val);
    }
  
    // Storing the sum of the left subtree
    if (curr.L != null)
        sum1 = curr.L.value;
  
    // Storing the sum of the right subtree
    if (curr.R != null)
        sum2 = curr.R.value;
  
    // Storing the sum of the children into
    // the node's value
    curr.value = sum1 + sum2;
    return;
}
  
// Function to find the sum of the
// values given by the range
function queryHelper(curr, a, b, L, R)
{
      
    // Return 0 if the root is null
    if (curr == null)
        return 0;
  
    // If the index is not overlapping
    // with the index, then the node
    // is not created. So sum is 0
    if (L > b || R < a)
        return 0;
  
    // If the index is completely overlapping
    // with the index, return the node's value
    if (L >= a && R <= b)
        return curr.value;
  
    let mid = L - parseInt((L - R) / 2, 10);
  
    // Return the sum of values stored
    // at the node's children
    return queryHelper(curr.L, a, b, L, mid) +
           queryHelper(curr.R, a, b, mid + 1, R);
}
  
// Function to call the queryHelper
// function to find the sum for
// the query
function query(L, R)
{
    return queryHelper(root, L, R, 1, 10);
}
  
// Function to call the UpdateHelper
// function for the point update
function update(index, value) 
{
    UpdateHelper(root, index, 1, 10, value);
}
  
// Function to perform the operations
// on the tree
function operations() 
{
      
    // Creating an empty tree
    root = getnode();
  
    // Update the value at position 1 to 10
    update(1, 10);
  
    // Update the value at position 3 to 5
    update(3, 5);
  
    // Finding sum for the range [2, 8]
    document.write(query(2, 8) + "</br>");
  
    // Finding sum for the range [1, 10]
    document.write(query(1, 10) + "</br>");
}
  
// Driver code
operations();
  
// This code is contributed by mukesh07
  
</script>
Producción: 

5
15

 

Complejidad temporal:  O(Q * logN) 
Espacio auxiliar: O(N) 

Tema relacionado: Árbol de segmentos

Publicación traducida automáticamente

Artículo escrito por akashsharma01 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *