Conteo de posibles restos para K en rangos dados para consultas Q

Dada una array arr[ ] que contiene N enteros positivos y un entero K y un vector de consultas Q . Se pueden realizar dos tipos de consultas:

  • En la consulta de tipo 1 , todos los elementos del índice l a r aumentan con el valor X. El formato de entrada de esta consulta: 1 lrx
  • En la consulta de tipo 2, para un entero K dado, imprima el recuento de todos los restos posibles si los elementos del índice l a r se dividen por k. El formato de entrada de esta consulta: 2 lr 

Ejemplos:

Entrada: arr[ ] = {7, 13, 5, 9, 16, 21}, K=4, vector< vector< int > >Q = { {2, 3, 5}, { 1, 0, 4, 1 }, {2, 2, 5} }
Salida:  1 2 0 0       
               0 2 2 0
Explicación : El primer tipo de consulta es 2. Entonces ,   para el índice 3 a 5, solo hay un elemento => (16) cuyo resto es 0 para dado k=4. Dos elementos cuyo resto es 1 => ( 9, 21 ). No hay ningún elemento cuyo resto sea 2 o 3. 
Después de la segunda consulta, será: {8, 14, 6, 10, 17, 21}. debido a que todos los elementos de la array se incrementan en 1 . 
En la tercera consulta para el índice 2 a 5, hay dos elementos cuyos residuos son 1 (17, 21) y 2 (6, 10). No hay elementos cuyo resto sea 0 o 3.

Entrada : arr[ ] = {6, 7, 8, 9, 10, 11}, k=5, vector< vector< int > >Q = { {2, 1, 1 } {2, 1, 3}, { 1, 1, 3, 2}, {2, 1, 3} }
Salida : 0 0 1 0 0 
             0 0 1 1 1 
            1 1 0 0 1     

 

Enfoque ingenuo: un enfoque simple es en la consulta de actualización, solo iterar a través de una array en un rango dado y actualizar el valor. Y para otro tipo de consulta, itere a través del rango dado y tome mod con k, mantenga la array hash para almacenar un recuento particular de ese resto. Pero la complejidad temporal de este método es O(N*Q*k)

Complejidad de tiempo: O(N*Q*k)
Espacio auxiliar: O(k), para mantener una array hash de tamaño k 

Enfoque eficiente: el enfoque anterior se puede optimizar utilizando el árbol de segmentos y la propagación perezosa , según la siguiente idea:

Intuición:

Aquí el número de consultas es alto y también hay consultas en las que se debe actualizar un rango de l a r. También tenemos que imprimir respuestas para el rango dado de l a r. 

Por lo tanto, estos dan sugerencias para usar el árbol de segmentos con propagación diferida , porque en este método podemos responder cada consulta en tiempo O (log (N)), es decir (Altura del árbol de segmentos). Junto con esto, usaremos la propagación diferida para actualizar los valores de rango de manera eficiente, porque en la propagación diferida, solo actualizamos el Node del árbol de segmentos que se requiere actualmente y propagará el valor al Node secundario, y actualizará este Node secundario, siempre que sea necesario. 

Para almacenar este valor de propagación,  

  • Use una array llamada perezosa (código de verificación) en lugar de un valor dado,  
  • Simplemente actualice un rango con val%k, porque el enfoque principal es el resto y esto no afectará la respuesta.
  • Use un árbol de segmentos 2D, de modo que cada Node del árbol de segmentos contenga una array de longitud K, para almacenar el recuento de todos los restos posibles.

Ilustración:

Para arr[]={1, 2, 5}, k=4, el árbol de segmentos se ve así:

así que básicamente el segmento de Node [ ind ] [ j ] contendrá el recuento de elementos cuyo resto es j correspondiente a un rango cubierto por el índice ind th del árbol de segmentos.

En otras palabras, si el ind -ésimo índice del árbol de segmento cubre el rango de a a b de la array, entonces segment[ ind ][ 0 ] representa, el conteo de elementos cuyo resto es 0 en el rango de a a b.

De manera similar, segment[ ind ][ j ] representará el recuento de elementos cuyo resto es j para K dado en el rango de a a b. 

Aquí, el truco principal es que si cualquier rango se actualiza por el valor x, entonces todos los elementos de la array de longitud K que se adjunta con el Node del árbol del segmento, harán el cambio cíclico correcto en x% k posiciones. 
Supongamos que si cualquier valor de rango se incrementa en 1, significa el conteo de elementos cuyo resto era 0 ahora se convierte en el conteo de elementos cuyo resto es 1. 

Antes de modificar ese Node, almacene el valor en la array temporal. 

Después de eso, modifique el Node del árbol de segmentos por segmento[ ind ][ (val+i)%k ]=temp[ i ]  

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

C++

// C++ program for Find count of all
// possible remainders for given  integer K
// in the given ranges for Q queries.
#include <bits/stdc++.h>
using namespace std;
#define MXX 100001
 
// to store propagate value
int lazy[4 * MXX];
int segment[4 * MXX][51];
int ans[51];
 
// build segment tree for given arr
void build(int arr[], int low, int high, int ind, int k)
{
    if (low == high) {
        int rem = arr[low] % k;
        // mark 1 at ind corresponding to remainder
        segment[ind][rem] = 1;
        return;
    }
 
    int mid = (low + high) >> 1;
    build(arr, low, mid, 2 * ind + 1, k);
    build(arr, mid + 1, high, 2 * ind + 2, k);
 
    // before returning, compute answer for
    // all possible remainders for node ind
    for (int i = 0; i < k; i++) {
        segment[ind][i] = segment[2 * ind + 1][i]
                          + segment[2 * ind + 2][i];
    }
}
 
// to update a range l to r
void update(int l, int r, int low, int high, int ind, int k,
            int val)
{
    lazy[ind] %= k;
 
    // if any value is pending than update it
    if (lazy[ind] != 0) {
        if (low != high) {
            // propagate pending value to its children
            lazy[2 * ind + 1] += lazy[ind];
            lazy[2 * ind + 2] += lazy[ind];
            lazy[2 * ind + 1] %= k;
            lazy[2 * ind + 2] %= k;
        }
        int incr = lazy[ind];
        // make temporary vector to store value
        // so we can perform cycle operation without
        // loosing actual values
        vector<int> temp(k);
 
        for (int i = 0; i < k; i++) {
            temp[i] = segment[ind][i];
        }
 
        // do cyclic shift operation
        for (int i = 0; i < k; i++) {
            segment[ind][(incr + i) % k] = temp[i];
        }
 
        // after done pending update mark it 0.
 
        lazy[ind] = 0;
    }
 
    // invalid range then return
    if (high < low || low > r || high < l)
        return;
 
    // the current range is subset of
    // our actual range so update value
    if (low >= l && high <= r) {
 
        val %= k;
 
        vector<int> temp(k);
 
        for (int i = 0; i < k; i++) {
            temp[i] = segment[ind][i];
        }
 
        for (int i = 0; i < k; i++) {
            segment[ind][(val + i) % k] = temp[i];
        }
        if (low != high) {
            lazy[2 * ind + 1] += val;
            lazy[2 * ind + 2] += val;
            lazy[2 * ind + 1] %= k;
            lazy[2 * ind + 2] %= k;
        }
 
        return;
    }
 
    int mid = (low + high) >> 1;
    // go to left and right side
    update(l, r, low, mid, 2 * ind + 1, k, val);
    update(l, r, mid + 1, high, 2 * ind + 2, k, val);
 
    // after updating and before returning,
    // calculate answer
    for (int i = 0; i < k; i++) {
        segment[ind][i] = segment[2 * ind + 1][i]
                          + segment[2 * ind + 2][i];
    }
}
 
// to compute answer of a query
// most of operation are same as update function
void query(int l, int r, int low, int high, int ind, int k)
{
    lazy[ind] %= k;
    if (lazy[ind] != 0) {
        if (low != high) {
            lazy[2 * ind + 1] += lazy[ind];
            lazy[2 * ind + 2] += lazy[ind];
            lazy[2 * ind + 1] %= k;
            lazy[2 * ind + 2] %= k;
        }
        int incr = lazy[ind];
        vector<int> temp(k);
 
        for (int i = 0; i < k; i++) {
            temp[i] = segment[ind][i];
        }
 
        for (int i = 0; i < k; i++) {
            segment[ind][(incr + i) % k] = temp[i];
        }
 
        lazy[ind] = 0;
    }
 
    if (high < low || low > r || high < l)
        return;
 
    // this range is subset of our actual
    // require range so compute answer for
    // this range
    if (low >= l && high <= r) {
        for (int i = 0; i < k; i++)
            ans[i] += segment[ind][i];
        return;
    }
 
    int mid = (low + high) >> 1;
 
    query(l, r, low, mid, 2 * ind + 1, k);
    query(l, r, mid + 1, high, 2 * ind + 2, k);
}
 
// after printing answer
// reset ans array
void print(int k)
{
    for (int i = 0; i < k; i++)
        cout << ans[i] << " ";
    cout << "\n";
}
void reset()
{
    for (int i = 0; i < 51; i++)
        ans[i] = 0;
}
 
int main()
{
 
    int arr[] = { 7, 13, 5, 9, 16, 21 };
    int n = sizeof(arr) / sizeof(arr[0]);
    int q = 3, k = 4;
 
    // build segment tree
    build(arr, 0, n - 1, 0, k);
 
    // first query
    int x, l = 3, r = 5;
    query(l, r, 0, n - 1, 0, k);
    print(k);
    reset();
 
    // second query
    l = 0, r = 4, x = 1;
    update(l, r, 0, n - 1, 0, k, x);
 
    // third query
    l = 2, r = 5;
    query(l, r, 0, n - 1, 0, k);
    print(k);
    reset();
 
    return 0;
}

Java

import java.util.*;
import java.io.*;
 
// Java program for Find count of all
// possible remainders for given  integer K
// in the given ranges for Q queries.
class GFG{
 
  public static int MXX = 100001;
 
  // to store propagate value
  public static int lazy[] = new int[4 * MXX];
  public static int segment[][] = new int[4 * MXX][51];
  public static int ans[] = new int[51];
 
  // build segment tree for given arr
  public static void build(int arr[], int low, int high, int ind, int k)
  {
    if (low == high) {
      int rem = arr[low] % k;
      // mark 1 at ind corresponding to remainder
      segment[ind][rem] = 1;
      return;
    }
 
    int mid = (low + high) >> 1;
    build(arr, low, mid, 2 * ind + 1, k);
    build(arr, mid + 1, high, 2 * ind + 2, k);
 
    // before returning, compute answer for
    // all possible remainders for node ind
    for (int i = 0; i < k; i++) {
      segment[ind][i] = segment[2 * ind + 1][i] + segment[2 * ind + 2][i];
    }
  }
 
  // to update a range l to r
  public static void update(int l, int r, int low, int high, int ind, int k, int val)
  {
    lazy[ind] %= k;
 
    // if any value is pending than update it
    if (lazy[ind] != 0) {
      if (low != high) {
        // propagate pending value to its children
        lazy[2 * ind + 1] += lazy[ind];
        lazy[2 * ind + 2] += lazy[ind];
        lazy[2 * ind + 1] %= k;
        lazy[2 * ind + 2] %= k;
      }
      int incr = lazy[ind];
 
      // make temporary array to store value
      // so we can perform cycle operation without
      // loosing actual values
      int temp[] = new int[k];
 
      for (int i = 0; i < k; i++) {
        temp[i] = segment[ind][i];
      }
 
      // do cyclic shift operation
      for (int i = 0; i < k; i++) {
        segment[ind][(incr + i) % k] = temp[i];
      }
 
      // after done pending update mark it 0.
      lazy[ind] = 0;
    }
 
    // invalid range then return
    if (high < low || low > r || high < l){
      return;
    }
 
    // the current range is subset of
    // our actual range so update value
    if (low >= l && high <= r) {
 
      val %= k;
 
      int temp[] = new int[k];
 
      for (int i = 0; i < k; i++) {
        temp[i] = segment[ind][i];
      }
 
      for (int i = 0 ; i < k ; i++) {
        segment[ind][(val + i) % k] = temp[i];
      }
      if (low != high) {
        lazy[2 * ind + 1] += val;
        lazy[2 * ind + 2] += val;
        lazy[2 * ind + 1] %= k;
        lazy[2 * ind + 2] %= k;
      }
 
      return;
    }
 
    int mid = (low + high) >> 1;
    // go to left and right side
    update(l, r, low, mid, 2 * ind + 1, k, val);
    update(l, r, mid + 1, high, 2 * ind + 2, k, val);
 
    // after updating and before returning,
    // calculate answer
    for (int i = 0; i < k; i++) {
      segment[ind][i] = segment[2 * ind + 1][i]
        + segment[2 * ind + 2][i];
    }
  }
 
  // to compute answer of a query
  // most of operation are same as update function
  public static void query(int l, int r, int low, int high, int ind, int k)
  {
    lazy[ind] %= k;
    if (lazy[ind] != 0) {
      if (low != high) {
        lazy[2 * ind + 1] += lazy[ind];
        lazy[2 * ind + 2] += lazy[ind];
        lazy[2 * ind + 1] %= k;
        lazy[2 * ind + 2] %= k;
      }
      int incr = lazy[ind];
      int temp[] = new int[k];
 
      for (int i = 0 ; i < k ; i++) {
        temp[i] = segment[ind][i];
      }
 
      for (int i = 0 ; i < k ; i++) {
        segment[ind][(incr + i) % k] = temp[i];
      }
 
      lazy[ind] = 0;
    }
 
    if (high < low || low > r || high < l)
      return;
 
    // this range is subset of our actual
    // require range so compute answer for
    // this range
    if (low >= l && high <= r) {
      for (int i = 0 ; i < k ; i++){
        ans[i] += segment[ind][i];
      }
      return;
    }
 
    int mid = (low + high) >> 1;
 
    query(l, r, low, mid, 2 * ind + 1, k);
    query(l, r, mid + 1, high, 2 * ind + 2, k);
  }
 
  // after printing answer
  // reset ans array
  public static void print(int k)
  {
    for (int i = 0; i < k; i++)
      System.out.print(ans[i] + " ");
    System.out.println("");
  }
 
  public static void reset()
  {
    for (int i = 0; i < 51; i++){
      ans[i] = 0;
    }
  }
 
  // Driver code
  public static void main(String args[])
  {
    int arr[] = {7, 13, 5, 9, 16, 21};
    int n = arr.length;
    int q = 3, k = 4;
 
    // build segment tree
    build(arr, 0, n-1, 0, k);
 
    // first query
    int x, l = 3, r = 5;
    query(l, r, 0, n-1, 0, k);
    print(k);
    reset();
 
    // second query
    l = 0;
    r = 4;
    x = 1;
    update(l, r, 0, n-1, 0, k, x);
 
    // third query
    l = 2;
    r = 5;
    query(l, r, 0, n-1, 0, k);
    print(k);
    reset();
  }
}
 
// This code is contributed by subhamgoyal2014.

Python3

# program for Find count of all
# possible remainders for given integer K
# in the given ranges for Q queries.
MXX = 100001
 
# to store propagate value
lazy = [0]*(4*MXX)
segment = [[0 for i in range(51)] for i in range(4*MXX)]
ans = [0]*51
 
# build segment tree for given arr
def build(arr, low, high, ind, k):
    if low == high:
        rem = arr[low] % k
         
        # mark 1 at ind corresponding to remainder
        segment[ind][rem] = 1
        return
    mid = (low+high) >> 1
    build(arr, low, mid, 2*ind+1, k)
    build(arr, mid+1, high, 2*ind+2, k)
     
    # Before returning, compute answer for
    # all possible remainders for node ind
    for i in range(k):
        segment[ind][i] = segment[2*ind+1][i]+segment[2*ind+2][i]
         
# function to update a range l to r
def update(l, r, low, high, ind, k, val):
    lazy[ind] %= k
     
    # if any value is pending than update it
    if lazy[ind] != 0:
        if low != high:
           
            # propagate pending value to its children
            lazy[2*ind+1] += lazy[ind]
            lazy[2*ind+2] += lazy[ind]
            lazy[2*ind+1] %= k
            lazy[2*ind+2] %= k
 
        incr = lazy[ind]
         
        # make temporary vector to store value
        # so we can perform cycle operation without
        # loosing actual values
        temp = [0]*k
        for i in range(k):
            temp[i] = segment[ind][i]
             
        # do cyclic shift operation
        for i in range(k):
            segment[ind][(incr+1) % k] = temp[i]
             
        # after done pending update mark it 0.
        lazy[ind] = 0
         
    # invalid range then return
    if high < low or low > r or high < l:
        return
       
    # the current range is subset of
    # our actual range so update value
    if low >= l and high <= r:
        val %= k
        temp = [0]*k
        for i in range(k):
            temp[i] = segment[ind][i]
        for i in range(k):
            segment[ind][(val+i) % k] = temp[i]
        if low != high:
            lazy[2 * ind + 1] += val
            lazy[2 * ind + 2] += val
            lazy[2 * ind + 1] %= k
            lazy[2 * ind + 2] %= k
        return
    mid = (low+high) >> 1
     
    # go to left and right side
    update(l, r, low, mid, 2*ind+1, k, val)
    update(l, r, mid+1, high, 2*ind+2, k, val)
     
    # after updating and before returning, calculate answer
    for i in range(k):
        segment[ind][i] = segment[2*ind+1][i]+segment[2*ind+2][i]
         
# to compute answer of a query
# most of operation are same as update function
def query(l, r, low, high, ind, k):
    lazy[ind] %= k
    if lazy[ind] != 0:
        if low != high:
            lazy[2 * ind + 1] += lazy[ind]
            lazy[2 * ind + 2] += lazy[ind]
            lazy[2 * ind + 1] %= k
            lazy[2 * ind + 2] %= k
        incr = lazy[ind]
        temp = [0]*k
        for i in range(k):
            temp[i] = segment[ind][i]
        for i in range(k):
            segment[ind][(incr+i) % k] = temp[i]
        lazy[ind] = 0
    if high < low or low > r or high < l:
        return
       
    # this range is subset of our actual
    # require range so compute answer for
    # this range
    if low >= l and high <= r:
        for i in range(k):
            ans[i] += segment[ind][i]
        return
    mid = (low+high) >> 1
    query(l, r, low, mid, 2*ind+1, k)
    query(l, r, mid+1, high, 2*ind+2, k)
     
# after printing answer
# reset ans array
def printing(k):
    for i in range(int(k)):
        print(ans[i], end=" ")
    print()
 
def reset():
    for i in range(51):
        ans[i] = 0
 
# Driver Code
if __name__ == '__main__':
    arr = [7, 13, 5, 9, 16, 21, 33, 12, 19]
    n = len(arr)
    q = 3
    k = 4
     
    # build segment tree
    build(arr, 0, n - 1, 0, k)
     
    # first query
    x = 0
    l = 3
    r = 5
    query(l, r, 0, n-1, 0, k)
    printing(k)
    reset()
     
    # second query
    l = 0
    r = 4
    x = 1
    update(l, r, 0, n - 1, 0, k, x)
     
    # third query
    l = 2
    r = 5
    query(l, r, 0, n - 1, 0, k)
    printing(k)
    reset()
'''This Code is contributed by RAJAT KUMAR'''
Producción

1 2 0 0 
0 2 2 0 

Complejidad de tiempo : O(Q*K*logN)
Espacio auxiliar : O(N*K)

Publicación traducida automáticamente

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