Informática Ideia 06 – Soma de Prefixos

Feito por Thiago Mota

Imagine o seguinte problema, dado um vetor de $$N$$ elementos e $$Q$$ intervalos $$[l, r]$$ queremos saber para cada uma das $$Q$$ perguntas a soma dos elementos da posição $$l$$ até $$r$$ no vetor.

A solução mais fácil seria andar pelo vetor de $$l$$ até $$r$$ para cada uma das perguntas:


// Ideias 06 – Soma de Prefixos
// Thiago Mota
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, q, v[maxn];
int main() {
cin >> n >> q;
for(int i = 1; i <= n; i++) {
cin >> v[i]; // Lendo o vetor
}
for(int i = 1; i <= q; i++) {
int l, r;
cin >> l >> r;
int soma = 0;
for(int j = l; j <= r; j++)
soma += v[j]; // Somando cada elemento de [l, r]
cout << soma << "\n";
}
return 0;
}

view raw

ideia_06_01.cpp

hosted with ❤ by GitHub

Sendo que isso fica em uma complexidade de $$O(N \cdot Q)$$, que para valores de $$10^5$$ receberia um TLE.

A solução rápida seria utilizando soma de prefixos, vamos olhar para o seguinte vetor: $$v = [2, 1, 3, 4, 7, 5, 3]$$, para resolver o problema da soma iremos definir um vetor de prefixo $$pref$$ de forma que $$pref(i)$$ vai guardar a soma de todos os elementos em $$v$$ de $$1$$ até $$i$$, no caso desse vetor o prefixo ficaria assim: $$pref = [2, 3, 6, 10, 17, 22, 25]$$, com esse vetor de prefixo podemos responder qualquer pergunta em um intervalo de $$[1, i]$$, mas caso olharmos para a soma da posição $$[3, 5]$$ podemos imaginar como sendo a soma de $$[1, 5]$$ menos a soma de $$[1, 2]$$, ou seja, utilizando o vetor de prefixo podemos calcular a soma para qualquer intervalo $$[l, r]$$, segue um exemplo:

$$v = [\textbf{2}, \textbf{1}, \textbf{3}, \textbf{4},\textbf{7}, 5, 3]$$ ($$soma = 17$$)

$$v = [\textbf{2}, \textbf{1}, 3, 4, 7, 5, 3]$$ ($$soma = 3$$)

$$v = [2, 1, \textbf{3}, \textbf{4},\textbf{7} , 5, 3]$$ ($$soma = 17-3$$)

Resumindo, para calcular a soma de $$[l, r]$$ podemos calcular a soma de $$[1, r]$$ e subtrair da soma de $$[1, l-1]$$, e para calcular essa soma podemos fazer $$pref(r) – pref(l-1)$$, segue o código:


// Ideias 06 – Soma de Prefixos
// Thiago Mota
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, q, v[maxn], pref[maxn];
int main() {
cin >> n >> q;
for(int i = 1; i <= n; i++) {
cin >> v[i]; // Lendo o vetor
}
pref[1] = v[1]; // Prefixo da posição 1 é ele mesmo
for(int i = 2; i <= n; i++) {
pref[i] = pref[i-1] + v[i]; // O prefixo de i é o prefixo de i-1 adicionado do elemento na posição i
}
for(int i = 1; i <= q; i++) {
int l, r;
cin >> l >> r;
if(l == 1) {
cout << pref[r] << "\n"; // Se o l for 1, podemos apenas imprimir o prefixo.
}else {
cout << pref[r] – pref[l-1] << "\n"; // Senão, iremos imprimir a soma de [1, r] – [1, l-1]
}
}
return 0;
}

view raw

ideia_06_02.cpp

hosted with ❤ by GitHub

A ideia de prefixo pode ser utilizada para multiplicação (utilizando divisão como operação oposta) ou qualquer outra operação que possua um inverso.

Exercícios para praticar:

OBI 2017 P1 – Segredo do Cofre