Дерево Фенвика
Дан массив чисел
Необходимо ответить на запросы о состоянии массива (например сумма элементов с i по j) и обработать события по его изменению (увеличить на x i-тый элемент)
Создадим дополнительный массив p. Пусть p[i] равно сумме всех элементов массива с i - I[i] + 1 по i, где I[x] - максимальная степень двойки, являющаяся делителем x.
I[0] = 1
I[1] = 1
I[2] = 2
I[3] = 1
I[4] = 4
...
p[0] = a[0]
p[1] = a[1]
p[2] = a[1] + a[2]
...
p[10] = a[9] + a[10]
p[11] = a[11]
p[12] = a[9] + a[10] + a[11] + a[12]
...
Несложно увидить, что сумму элементов от 0 по i можно найти так:
while (i >= 0)
{
sum += p[i];
i -= I[i];
}
Если мы увеличим на x i-тый элемент, то необходимо увеличить все p[j], которые содержат a[i]. Рассмотрев примеры можно прийти к выводу, что последовательность всех j строится следующим образом:
j0 = i
j1 = j0 + I[j0]
j2 = j1 + I[j1]
...
Очевидно, что и запрос и обработка события имеют асимптотику O(log n).
Замечу, что дерево Фенвика можно легко реализовать на двумерный случай. Для этого необходимо просто добавить в функции обработки запроса и события ещё одну переменную (x, y) и работать с ними аналогично.
Листинг С++
class fenwick_tree
{
private :
int n;
int *I;
int *p;
public:
fenwick_tree (int _n = 1)
{
int i, j;
n = _n;
I = new int[n];
I[0] = 1;
for (i = 1; i < n; ++ i)
if (i & 1)
I[i] = 1;
else
I[i] = I[i >> 1] << 1;
p = new int[n];
for (i = 0; i < n; ++ i)
p[i] = 0;
}
int sum (int r)
{
int s = 0;
int i = r;
while (i >= 0)
{
s += p[i];
i -= I[i];
}
return s;
}
int sum (int l, int r)
{
return sum (r) - (l == 0 ? 0 : sum (l - 1));
}
void add (int _i, int x)
{
int i = _i;
if (i == 0)
{
p[0] += x;
return ;
}
while (i < n)
{
p[i] += x;
i += I[i];
}
}
};
13:55
04.02.2010
По всем вопросам обращаться: rumterg@gmail.com