Processing math: 100%

Tabla de Contenidos

Tablita aditiva

Supongamos que queremos resolver el siguiente problema:

Problema: Tenemos un vector A de n números enteros. Nos llegan q consultas que preguntan por la suma de los elementos de un intervalo del vector. Es decir, cada consulta consiste de dos enteros a,b con 0a<bn y debemos responder la suma de los elementos de todas las posiciones i con ai<b. Queremos responder todas las consultas en O(n+q).

Por ejemplo, si A = [1, 2, 4, 0, -3, 2], estas serían posibles consultas y sus respectivas respuestas:

Solución: La idea será crear una estructura auxiliar que me permita responder rápidamente una consulta sin la necesidad de recorrer todo el intervalo. La estructura que usaremos será un vector S de n+1 posiciones donde guardaremos las suma de algunos intervalos estratégicamente elegidos. En concreto, en la posición i del vector S guardamos la suma de los números de A para todas las posiciones menores a i.

Por ejemplo, si nuevamente, el vector original es [1, 2, 4, 0, -3, 2] entonces S será [0, 1, 3, 7, 7, 4, 6] pues:

Supongamos que tenemos nuestro vector S calculado ¿Cómo nos ayuda S a responder las consultas rápido? Si nos llega la consula a, b, queremos sumar los números en posiciones menores a b y mayores o iguales a a. En S[b] tenemos la suma de los números en posiciones menores a b, pero estamos sumando números de más. Nos sobran los números en posiciones menores a a ¡Pero esto es justamente S[a]! Luego, podemos dar la respuesta como S[b] - S[a].

s[b]A[0]+A[1]++A[a1]s[a]+A[a]+A[a+1]++A[b1]s[b]s[a]

Con esto podemos responder cada consulta en O(1), tardando en total O(q) en responder todas. Todavía nos queda el problema de calcular S eficientemente, ya que el algoritmo que consiste en calcular cada elemento de S sumando todos los números del intervalo correspondiente es O(n2).

La observación clave es que si queremos calcular el elemento i de S podemos aprovechar que ya calculamos el i1 (si i>0), S[i-1] tiene la suma de los números en posiciones menores a la i1, el único que me falta sumar para conseguir S[i] (las posiciones menores a i) es el número de la posición i1. Luego, S[i] = S[i-1] + A[i-1].

s[i]A[0]+A[1]++A[i2]s[i1]+A[i1]

Usando esta fórmula y que S[0] = 0, podemos calcular el vector S en O(n).

Implementación

void sumasSubintervalos(vector<long long> A){
	int n = A.size(); //tamaño del vector
        vector<long long> S(n+1, 0); //S de tamaño n+1 inicializado en 0
        for(int i = 1; i <= n; i++) {
            S[i] = S[i-1] + A[i-1];
        }
 
        int q; //cantidad de consultas
        cin >> q;
        for(int i = 0; i < q; i++) {
            int a, b;
            cin >> a >> b;
            cout << S[b] - S[a];
        }
}

El caso de dos dimensiones

Problema: Supongamos ahora que en lugar de un vector, tenemos una matriz bidimensional M de tamaño n×m y ahora las consulas en lugar de pedir la suma de un intervalos, piden la suma de un rectángulo de números de la matriz. Es decir, las q consultas nos dan cuatro enteros a,b,c,d con 0a<cn y 0b<dm y debemos responder la suma de los números en el rectángulo de [a,c)×[b,d) ó, lo que es lo mismo, la suma de las posiciones (i,j) de la matriz con ai<c y bj<d. Queremos ahora responder todas las consultas en O(nm+q).

Solución: Nuevamente vamos a crear una estructura que guarde la suma de algunos rectángulos especiales. Esta vez, guardamos una matriz S de tamaño (n+1)×(m+1) donde cada posición (i,j) guarda la suma del rectángulo [0,i)×[0,j), es decir, el rectángulo de vértice superior izquierdo (0,0) y vértice inferior derecho (i,j).

Ahora, dada una consulta a,b,c,d, podemos responder la consulta en O(1) usando S como S[c][d] - S[a][d] - S[c][b] + S[a][b].

Para calcular S eficientemente, hay varias maneras, una de ellas es notar que S[i][j] = S[i-1][j] + S[i][j-1] - S[i-1][j-1] + A[i-1][j-1] (si i,j>0).

FIXME: Agregar dibujos explicativos.

Usando estas dos observaciones podemos calcular S en O(nm) y responder todas las consultas en O(q).

Implementación

void sumasSubrectangulos(vector<vector<long long>> M){
	int n = M.size(); //cantidad de filas
        int m = M[0].size(); //cantidad de columnas (ojo si n = 0)
        vector<vector<long long>> S(n+1, vector<long long>(m+1, 0)); //S de tamaño n+1 x m+1 inicializado en 0
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                S[i][j] = S[i-1][j] + S[i][j-1] - S[i-1][j-1] + A[i-1][j-1];
            }
        }
 
        int q; //cantidad de consultas
        cin >> q;
        for(int i = 0; i < q; i++) {
            int a, b, c, d;
            cin >> a >> b >> c >> d;
            cout << S[c][d] - S[a][d] - S[c][b] + S[a][b];
        }
}

FIXME: Explicar el problema en más dimensiones.

Problemas