Técnica de descomposición Sqrt (o raíz cuadrada) | Serie 1 (Introducción)

La técnica de descomposición Sqrt (o raíz cuadrada) es una de las técnicas de optimización de consultas más comunes que utilizan los programadores de la competencia . Esta técnica nos ayuda a reducir la complejidad del tiempo en un factor de sqrt(n)
El concepto clave de esta técnica es descomponer una array dada en pequeños fragmentos específicamente de tamaño sqrt(n).
Digamos que tenemos una array de n elementos y la descomponemos en pequeños fragmentos de tamaño sqrt(n). Tendremos exactamente sqrt(n) tales trozos siempre que n sea un cuadrado perfecto. Por lo tanto, ahora nuestra array en n elementos se descompone en bloques sqrt (n), donde cada bloque contiene elementos sqrt (n) (suponiendo que el tamaño de la array es un cuadrado perfecto).
Consideremos estos fragmentos o bloques como una array individual, cada uno de los cuales contiene elementos sqrt (n) y ha calculado la respuesta deseada (de acuerdo con su problema) individualmente para todos los fragmentos. Ahora, debe responder ciertas consultas que le piden la respuesta de los elementos en el rango l a r (l y r son índices iniciales y finales de la array) en la array original de tamaño n.
El enfoque ingenuo es simplemente iterar sobre cada elemento en el rango de l a r y calcular su respuesta correspondiente. Por lo tanto, la Complejidad de Tiempo por consulta será O(n).
Truco de descomposición Sqrt:Como ya hemos calculado previamente la respuesta para todos los fragmentos individuales y ahora necesitamos responder las consultas en el rango l a r. Ahora podemos simplemente combinar las respuestas de los fragmentos que se encuentran entre el rango de l a r en la array original. Entonces, si observamos cuidadosamente aquí, estamos saltando sqrt (n) pasos a la vez en lugar de saltar 1 paso a la vez como se hace en un enfoque ingenuo. Analicemos su Complejidad de tiempo e implementación considerando el siguiente problema: – 
 

Problem :
Given an array of n elements. We need to answer q 
queries telling the sum of elements in range l to 
r in the array. Also the array is not static i.e 
the values are changed via some point update query.

Range Sum Queries are of form : Q l r , 
where l is the starting index r is the ending 
index

Point update Query is of form : U idx val, 
where idx is the index to update val is the 
updated value

Consideremos que tenemos un arreglo de 9 elementos. 
A[] = {1, 5, 2, 4, 6, 1, 3, 5, 7}
Descompongamos esta array en bloques sqrt(9), donde cada bloque contendrá la suma de los elementos que se encuentran en él. Por lo tanto, ahora nuestra array descompuesta se vería así:
 

Sqrt decomposition of given array

Hasta ahora hemos construido la array descompuesta de bloques sqrt(9) y ahora necesitamos imprimir la suma de elementos en un rango dado. Entonces, primero veamos dos tipos básicos de superposición que una consulta de rango puede tener en nuestra array:
 
 

Consulta de rango de tipo 1 (el rango dado está en los límites del bloque):

sqrt2

En este tipo de consulta, el rango puede cubrir totalmente los bloques sqrt continuos. Entonces podemos responder fácilmente a la suma de valores en este rango como la suma de bloques completamente superpuestos. 
Entonces, la respuesta a la consulta anterior en la imagen descrita será: ans = 11 + 15 = 26 
Complejidad de tiempo: en el peor de los casos, nuestro rango puede ser de 0 a n-1 (donde n es el tamaño de la array y suponiendo que n sea un perfecto cuadrado). En este caso, todos los bloques están completamente superpuestos por nuestro rango de consulta. Por lo tanto, para responder a esta consulta necesitamos iterar sobre todos los bloques descompuestos de la array y sabemos que el número de bloques = sqrt(n). Por lo tanto, la complejidad para este tipo de consulta será O(sqrt(n)) en el peor de los casos.
 
 

Consulta de rango de tipo 2 (el rango dado NO está en los límites)

Query 3

Podemos tratar este tipo de consultas sumando los datos de los bloques descompuestos completamente superpuestos que se encuentran en el rango de consulta y luego sumando los elementos uno por uno de la array original cuyo bloque correspondiente no está completamente superpuesto por el rango de consulta. 
Entonces, la respuesta a la consulta anterior en la imagen descrita será: ans = 5 + 2 + 11 + 3 = 21 
Complejidad de tiempo:Consideremos una consulta [l = 1 y r = n-2] (n es el tamaño de la array y tiene una indexación basada en 0). Por lo tanto, para esta consulta exactamente ( sqrt(n) – 2 ) los bloques se superpondrán por completo, mientras que el primer y el último bloque se superpondrán parcialmente y solo quedará un elemento fuera del rango de superposición. Por lo tanto, los bloques completamente superpuestos se pueden resumir en ( sqrt(n) – 2 ) ~ sqrt(n) iteraciones, mientras que el primer y el último bloque deben atravesarse uno por uno por separado. Pero como sabemos que la cantidad de elementos en cada bloque está en max sqrt (n), para resumir el último bloque individualmente, debemos hacer, 
(sqrt (n) -1) ~ sqrt (n) iteraciones y lo mismo para el último bloquear. 
Entonces, la Complejidad total = O(sqrt(n)) + O(sqrt(n)) + O(sqrt(n)) = O(3*sqrt(N)) = O(sqrt(n))
 

Consultas de actualización (actualización de puntos):

En esta consulta, simplemente encontramos el bloque en el que se encuentra el índice dado, luego restamos su valor anterior y agregamos el nuevo valor actualizado según la consulta de actualización de puntos. 
Complejidad de tiempo : O(1) 
 

Implementación:

La implementación del truco anterior se da a continuación. 
 

C++

// C++ program to demonstrate working of Square Root
// Decomposition.
#include "iostream"
#include "math.h"
using namespace std;
 
#define MAXN 10000
#define SQRSIZE  100
 
int arr[MAXN];               // original array
int block[SQRSIZE];          // decomposed array
int blk_sz;                      // block size
 
// Time Complexity : O(1)
void update(int idx, int val)
{
    int blockNumber = idx / blk_sz;
    block[blockNumber] += val - arr[idx];
    arr[idx] = val;
}
 
// Time Complexity : O(sqrt(n))
int query(int l, int r)
{
    int sum = 0;
    while (l<r and l%blk_sz!=0 and l!=0)
    {
        // traversing first block in range
        sum += arr[l];
        l++;
    }
    while (l+blk_sz-1 <= r)
    {
        // traversing completely overlapped blocks in range
        sum += block[l/blk_sz];
        l += blk_sz;
    }
    while (l<=r)
    {
        // traversing last block in range
        sum += arr[l];
        l++;
    }
    return sum;
}
 
// Fills values in input[]
void preprocess(int input[], int n)
{
    // initiating block pointer
    int blk_idx = -1;
 
    // calculating size of block
    blk_sz = sqrt(n);
 
    // building the decomposed array
    for (int i=0; i<n; i++)
    {
        arr[i] = input[i];
        if (i%blk_sz == 0)
        {
            // entering next block
            // incrementing block pointer
            blk_idx++;
        }
        block[blk_idx] += arr[i];
    }
}
 
// Driver code
int main()
{
    // We have used separate array for input because
    // the purpose of this code is to explain SQRT
    // decomposition in competitive programming where
    // we have multiple inputs.
    int input[] = {1, 5, 2, 4, 6, 1, 3, 5, 7, 10};
    int n = sizeof(input)/sizeof(input[0]);
 
    preprocess(input, n);
 
    cout << "query(3,8) : " << query(3, 8) << endl;
    cout << "query(1,6) : " << query(1, 6) << endl;
    update(8, 0);
    cout << "query(8,8) : " << query(8, 8) << endl;
    return 0;
}

Java

// Java program to demonstrate working of
// Square Root Decomposition.
import java.util.*;
 
class GFG
{
 
static int MAXN = 10000;
static int SQRSIZE = 100;
 
static int []arr = new int[MAXN];             // original array
static int []block = new int[SQRSIZE];         // decomposed array
static int blk_sz;                             // block size
 
// Time Complexity : O(1)
static void update(int idx, int val)
{
    int blockNumber = idx / blk_sz;
    block[blockNumber] += val - arr[idx];
    arr[idx] = val;
}
 
// Time Complexity : O(sqrt(n))
static int query(int l, int r)
{
    int sum = 0;
    while (l < r && l % blk_sz != 0 && l != 0)
    {
        // traversing first block in range
        sum += arr[l];
        l++;
    }
    while (l+blk_sz-1 <= r)
    {
        // traversing completely
        // overlapped blocks in range
        sum += block[l / blk_sz];
        l += blk_sz;
    }
    while (l <= r)
    {
        // traversing last block in range
        sum += arr[l];
        l++;
    }
    return sum;
}
 
// Fills values in input[]
static void preprocess(int input[], int n)
{
    // initiating block pointer
    int blk_idx = -1;
 
    // calculating size of block
    blk_sz = (int) Math.sqrt(n);
 
    // building the decomposed array
    for (int i = 0; i < n; i++)
    {
        arr[i] = input[i];
        if (i % blk_sz == 0)
        {
            // entering next block
            // incrementing block pointer
            blk_idx++;
        }
        block[blk_idx] += arr[i];
    }
}
 
// Driver code
public static void main(String[] args)
{
     
    // We have used separate array for input because
    // the purpose of this code is to explain SQRT
    // decomposition in competitive programming where
    // we have multiple inputs.
    int input[] = {1, 5, 2, 4, 6, 1, 3, 5, 7, 10};
    int n = input.length;
 
    preprocess(input, n);
 
    System.out.println("query(3, 8) : " +
                        query(3, 8));
    System.out.println("query(1, 6) : " +
                        query(1, 6));
    update(8, 0);
    System.out.println("query(8, 8) : " +
                        query(8, 8));
}
}
 
// This code is contributed by PrinciRaj1992

Python 3

# Python 3 program to demonstrate working of Square Root
# Decomposition.
from math import sqrt
 
MAXN = 10000
SQRSIZE = 100
 
arr = [0]*(MAXN)         # original array
block = [0]*(SQRSIZE)     # decomposed array
blk_sz = 0                 # block size
 
# Time Complexity : O(1)
def update(idx, val):
    blockNumber = idx // blk_sz
    block[blockNumber] += val - arr[idx]
    arr[idx] = val
 
# Time Complexity : O(sqrt(n))
def query(l, r):
    sum = 0
    while (l < r and l % blk_sz != 0 and l != 0):
         
        # traversing first block in range
        sum += arr[l]
        l += 1
     
    while (l + blk_sz - 1 <= r):
         
        # traversing completely overlapped blocks in range
        sum += block[l//blk_sz]
        l += blk_sz
     
    while (l <= r):
         
        # traversing last block in range
        sum += arr[l]
        l += 1
     
    return sum
     
# Fills values in input[]
def preprocess(input, n):
     
    # initiating block pointer
    blk_idx = -1
 
    # calculating size of block
    global blk_sz
    blk_sz = int(sqrt(n))
 
    # building the decomposed array
    for i in range(n):
        arr[i] = input[i];
        if (i % blk_sz == 0):
             
            # entering next block
            # incrementing block pointer
            blk_idx += 1;
         
        block[blk_idx] += arr[i]
 
# Driver code
 
# We have used separate array for input because
# the purpose of this code is to explain SQRT
# decomposition in competitive programming where
# we have multiple inputs.
input= [1, 5, 2, 4, 6, 1, 3, 5, 7, 10]
n = len(input)
 
preprocess(input, n)
 
print("query(3,8) : ",query(3, 8))
print("query(1,6) : ",query(1, 6))
update(8, 0)
print("query(8,8) : ",query(8, 8))
 
# This code is contributed by Sanjit_Prasad

C#

// C# program to demonstrate working of
// Square Root Decomposition.
using System;
     
class GFG
{
static int MAXN = 10000;
static int SQRSIZE = 100;
 
static int []arr = new int[MAXN];             // original array
static int []block = new int[SQRSIZE];         // decomposed array
static int blk_sz;                             // block size
 
// Time Complexity : O(1)
static void update(int idx, int val)
{
    int blockNumber = idx / blk_sz;
    block[blockNumber] += val - arr[idx];
    arr[idx] = val;
}
 
// Time Complexity : O(sqrt(n))
static int query(int l, int r)
{
    int sum = 0;
    while (l < r && l % blk_sz != 0 && l != 0)
    {
        // traversing first block in range
        sum += arr[l];
        l++;
    }
    while (l + blk_sz - 1 <= r)
    {
        // traversing completely
        // overlapped blocks in range
        sum += block[l / blk_sz];
        l += blk_sz;
    }
    while (l <= r)
    {
        // traversing last block in range
        sum += arr[l];
        l++;
    }
    return sum;
}
 
// Fills values in input[]
static void preprocess(int []input, int n)
{
    // initiating block pointer
    int blk_idx = -1;
 
    // calculating size of block
    blk_sz = (int) Math.Sqrt(n);
 
    // building the decomposed array
    for (int i = 0; i < n; i++)
    {
        arr[i] = input[i];
        if (i % blk_sz == 0)
        {
            // entering next block
            // incrementing block pointer
            blk_idx++;
        }
        block[blk_idx] += arr[i];
    }
}
 
// Driver code
public static void Main(String[] args)
{
     
    // We have used separate array for input because
    // the purpose of this code is to explain SQRT
    // decomposition in competitive programming where
    // we have multiple inputs.
    int []input = {1, 5, 2, 4, 6, 1, 3, 5, 7, 10};
    int n = input.Length;
 
    preprocess(input, n);
 
    Console.WriteLine("query(3, 8) : " +
                       query(3, 8));
    Console.WriteLine("query(1, 6) : " +
                       query(1, 6));
    update(8, 0);
    Console.WriteLine("query(8, 8) : " +
                       query(8, 8));
}
}
 
// This code is contributed by 29AjayKumar

Javascript

<script>
// Javascript program to demonstrate working of
// Square Root Decomposition.
 
let MAXN = 10000;
let SQRSIZE = 100;
 
let arr = new Array(MAXN);
for(let i = 0; i < MAXN; i++)
{
    arr[i] = 0;
}
 
let block = new Array(SQRSIZE);
for(let i = 0; i < SQRSIZE; i++)
{
    block[i] = 0;
}
 
let blk_sz;    
 
// Time Complexity : O(1)
function update(idx,val)
{
    let blockNumber = idx / blk_sz;
    block[blockNumber] += val - arr[idx];
    arr[idx] = val;
}
 
// Time Complexity : O(sqrt(n))
function query(l, r)
{
    let sum = 0;
    while (l < r && l % blk_sz != 0 && l != 0)
    {
        // traversing first block in range
        sum += arr[l];
        l++;
    }
    while (l+blk_sz-1 <= r)
    {
        // traversing completely
        // overlapped blocks in range
        sum += block[l / blk_sz];
        l += blk_sz;
    }
    while (l <= r)
    {
        // traversing last block in range
        sum += arr[l];
        l++;
    }
    return sum;
}
 
// Fills values in input[]
function preprocess(input, n)
{
    // initiating block pointer
    let blk_idx = -1;
   
    // calculating size of block
    blk_sz = Math.floor( Math.sqrt(n));
   
    // building the decomposed array
    for (let i = 0; i < n; i++)
    {
        arr[i] = input[i];
        if (i % blk_sz == 0)
        {
         
            // entering next block
            // incrementing block pointer
            blk_idx++;
        }
        block[blk_idx] += arr[i];
    }
}
 
// Driver code
// We have used separate array for input because
    // the purpose of this code is to explain SQRT
    // decomposition in competitive programming where
    // we have multiple inputs.
    let input = [1, 5, 2, 4, 6, 1, 3, 5, 7, 10];
    let n = input.length;
   
    preprocess(input, n);
   
    document.write("query(3, 8) : " +
                        query(3, 8)+"<br>");
    document.write("query(1, 6) : " +
                        query(1, 6)+"<br>");
    update(8, 0);
    document.write("query(8, 8) : " +
                        query(8, 8)+"<br>");
 
// This code is contributed by rag2127
</script>
Producción

query(3,8) : 26
query(1,6) : 21
query(8,8) : 0

Complejidad de tiempo: O(n)

Espacio auxiliar : O (MAXN)
Nota: el código anterior funciona incluso si n no es un cuadrado perfecto. En el caso, el último bloque contendrá aún menos cantidad de elementos que sqrt(n), reduciendo así la cantidad de iteraciones. 
Digamos n = 10. En este caso, tendremos 4 bloques, los primeros tres bloques de tamaño 3 y el último bloque de tamaño 1.
Este artículo es una contribución de Nitish Kumar . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 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 *