Aula por Pedro Victor

Soma de Prefixos é uma técnica para saber a soma de um intervalo entre dois índices $[l,r]$ de algum vetor de maneira mais rápida. Digamos que temos um vetor $V$ com $N$ elementos e também temos $M$ perguntas sobre a soma do intervalo entre $l$ e $r$. Sendo assim, a solução mais simples seria ir somando todos os números de $l$ a $r$, como no exemplo abaixo:

#include <bits/stdc++.h>
using namespace std;
int v[1000000];
int n,m;
int main()
{
cin >> n >> m; //Quantidade de elementos no vetor v e a quantidade de perguntas m
for(int i = 1 ; i <= n ; i++)
{
cin >> v[i]; //O número na posição i do vetor
}
for(int i = 1; i <= m ; i++)
{
int l,r; //O intervalo de l até r
cin >> l >> r;
//Percorro esse intervalo somando todos os elementos em uma variavel resposta
int resposta = 0;
for(int j = l ; j <= r ; j++)
{
resposta = resposta + v[j];
}
cout << "No intervalo de" << l << " ate " << r << " a soma e " << resposta << "\n";
}
return 0;
}

Porém, dessa maneira, a complexidade seria $O(N*M)$, podendo exceder o limite de tempo na maioria dos casos. Para otimizar esse código, devemos usar a Soma de Prefixos.

Primeiro temos um vetor com os elementos $V = (1,3,2,4,9,8,1)$ e utilizaremos um vetor auxiliar (chamaremos de $aux$) para guardar a soma de todos os elementos de $1$ até $i$, isto é, o prefixo de $i$. Neste caso, ficaria $aux = (1,4,6,10,19,27,28)$. Para saber o intervalo $[l,r]$, simplesmente iríamos pegar a soma do intervalo $[1,r]$ (ou seja, o prefixo de $r$) e diminuir dela o prefixo de $l-1$. Por exemplo, para o intervalo $[4,7]$, pegaríamos a soma de $[1 ,3] = 6$ e subtrairíamos isso da soma de $[1,7] = 28$, assim sendo $28 – 6 = 22$, que é o resultado da soma de $[4,7]$. Dessa maneira, o código ficaria com uma complexidade $O(Q)$:

#include <bits/stdc++.h>
using namespace std;
int v[1000000], aux[1000000];
int n,m;
int main()
{
cin >> n >> m; //Quantidade de elementos no vetor v e a quantidade de perguntas
for(int i = 1 ; i <= n ; i++)
{
cin >> v[i]; //O número na posição i do vetor
}
//igualamos as primeiras posições e percorremos da segunda até a ultima
aux[1] = v[1];
for(int i = 2 ; i <= n ; ++i)
{
//a posição i do aux é a soma de v[1] até v[i-1] que seria o aux[i-1] + v[i]
aux[i] = v[i] + aux[i-1];
}
for(int i = 1 ; i <= m ; i++)
{
int l,r; //O intervalo [l,r] na qual queremos a soma
cin >> l >> r;
//e diminuimos o aux[l-1] que seia a soma de [1,l-1] do aux[r] que seria a soma de [1,r] colocando elas em uma variavel resposta
int resposta;
resposta = aux[r] - aux[l-1];
cout << "No intervalo de " << l << " ate " << r << " a soma e " << resposta << "\n";
}
return 0;
}
view raw prefix_sum.cpp hosted with ❤ by GitHub

Aqui vão alguns exercícios onde você pode treinar essa ideia: