Дерево Фенвика: различия между версиями
Ctrlalt (обсуждение | вклад) Нет описания правки |
Ctrlalt (обсуждение | вклад) (→Ссылки) |
||
Строка 119: | Строка 119: | ||
Теория: | Теория: | ||
:* [[Варианты дерева Фенвика]] | :* [[Варианты дерева Фенвика]] | ||
:* [http://e-maxx.ru/algo/fenwick_tree e-maxx.ru | :* [http://e-maxx.ru/algo/fenwick_tree e-maxx.ru — Дерево Фенвика] | ||
:* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%B0%D1%8F_%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85#.D0.94.D0.B5.D1.80.D0.B5.D0.B2.D0.BE_.D0.A4.D0.B5.D0.BD.D0.B2.D0.B8.D0.BA.D0.B0 neerc.ifmo.ru/wiki — Дерево Фенвика] | :* [http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%B0%D1%8F_%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0,_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%B8_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85#.D0.94.D0.B5.D1.80.D0.B5.D0.B2.D0.BE_.D0.A4.D0.B5.D0.BD.D0.B2.D0.B8.D0.BA.D0.B0 neerc.ifmo.ru/wiki — Дерево Фенвика] | ||
:* [ | :* [https://brestprog.by/topics/fenwicktree/ brestprog.by — Дерево Фенвика] | ||
:* [http://cppalgo.blogspot.ru/2011/02/fenwick-tree.html cppalgo.blogspot.com | :* [https://algorithmica.org/ru/fenwick algorithmica.org — Дерево Фенвика] | ||
:* [ | :* [http://cppalgo.blogspot.ru/2011/02/fenwick-tree.html cppalgo.blogspot.com — Дерево Фенвика] | ||
:* [https://informatics.mccme.ru/mod/book/view.php?id=491&chapterid=182 Лахно А. Дерево Фенвика] | |||
Демонстрация: | Демонстрация: | ||
:* [ | :* [https://visualgo.net/en/fenwicktree VisuAlgo — Fenwick Tree] | ||
Код: | Код: | ||
:* [ | :* [https://github.com/indy256/codelibrary/blob/master/cpp/structures/fenwick_tree.cpp CodeLibrary — Fenwick tree] | ||
:* [http://github.com/ADJA/algos/blob/master/DataStructures/FenwickTree.cpp Algos | :* [https://github.com/indy256/codelibrary/blob/master/cpp/structures/fenwick_tree_interval.cpp CodeLibrary — Fenwick tree interval] | ||
:* [http://github.com/ADJA/algos/blob/master/DataStructures/FenwickTree2D.cpp Algos | :* [http://github.com/ADJA/algos/blob/master/DataStructures/FenwickTree.cpp Algos — Fenwick tree] | ||
:* [http://github.com/ADJA/algos/blob/master/DataStructures/FenwickTree2D.cpp Algos — Fenwick tree 2D] | |||
Задачи: | Задачи: | ||
:* [[:Категория: Задачи: Дерево Фенвика|Задачи: Дерево Фенвика]] | :* [[:Категория: Задачи: Дерево Фенвика|Задачи: Дерево Фенвика]] | ||
[[Category:Структуры данных для задач RSQ/RMQ]] | [[Category:Структуры данных для задач RSQ/RMQ]] |
Текущая версия от 12:19, 11 сентября 2020
Запрос суммы на отрезке, запрос добавления к отдельному элементу
Пусть имеется массив f[], каждый элемент которого хранит частичную сумму элементов массива a[], а именно а[i] = a[F(i)] + a[F(i) + 1] + ... + a[i - 1] + a[i], где F() — определённая функция.
Тогда сумма от 0 до r вычисляется следующим образом:
int query(int r) { int res = 0; for (int i = r; i >= 0; i = F(i) - 1) res += f[i]; return res; }
Пусть F(i) = i & (i + 1):
i = xx...x100...011...1 F(i) = xx...x100...000...0 F(i) - 1 = xx...x011...111...1
Можно видеть, что каждая операция i = (i & (i + 1)) - 1 будет обнулять в i один единичный разряд, что обеспечит логарифмическую сложность операции суммы.
Если некоторый элемент a[i] изменился, то должны таким же образом измениться все элементы f[j], где F(j) <= i <= j. После некоторых вычислений можно убедиться, что данным ограничениям удовлетворяет индекс i, а также все индексы, получаемые из i последовательностью изменений младшего нулевого разряда на единичный, то есть применений операции i |= (i + 1).
int query(int r) { int res = 0; for (int i = r; i >= 0; i = (i & (i + 1)) - 1) res += f[i]; return res; } void modify(int r, int val) { for (int i = r; i < MAX_SIZE; i |= i + 1) f[i] += val; } int sum(int l, int r) { return query(r) - (l ? query(l - 1) : 0); } void add(int pos, int val) { modify(pos, val); }
Запрос отдельного элемента, запрос добавления на отрезке
Заменим массив a[] на массив deltaA[] по следующему правилу: deltaA[0] = a[0], deltaA[i] = a[i] - a[i - 1]. Построим дерево Фенвика по массиву deltaA[].
Тогда query(i) равно a[i], а вызов modify(pos, val) добавит val ко всем элементам a[], у которых i >= pos. Следовательно, чтобы увеличить элементы с индексами от l до r, нужно вызвать modify(l, val) и modify(r + 1, -val).
int get(int pos) { return query(pos); } void add(int l, int r, int val) { modify(l, val); modify(r + 1, -val); }
Запрос минимума на отрезке, запрос добавления к отдельному элементу
- Dima M., Ceterchi R. Efficient Range Minimum Queries using Binary Indexed Trees
- neerc.ifmo.ru/wiki — Встречное дерево Фенвика
Запрос суммы на отрезке, запрос добавления на отрезке
Метод P. Mishra
Пусть имеются деревья Фенвика T и Tx, и пусть (Tx.query(i) * i + T.query(i)) — сумма элементов массива a[] от a[0] до a[i]. Далее, пусть элементы массива a[] с индексами от l до r увеличились на величину val. Рассмотрим, как при этом изменится сумма элементов массива a[] от a[0] до a[i]:
- Если i < l, то сумма не изменилась, и значение (Tx.query(i) * i + T.query(i)) ей соответствует.
- Если l <= i <= r, то сумма увеличилась на (val * (i - l + 1)). Следовательно, к ячейкам Tx нужно добавить val, а к ячейкам T — (val * (-l + 1)).
- Если i > r, то сумма увеличилась на (val * (r - l + 1)). Следовательно, ячейки Tx изменять не нужно, а к ячейкам T нужно добавить (val * (r - l + 1)).
int prefixSum(int r) { return Tx.query(r) * r + T.query(r); } int sum(int l, int r) { return prefixSum(r) - (l ? prefixSum(l - 1) : 0); } void add(int l, int r, int val) { Tx.modify(l, val); Tx.modify(r + 1, -val); //Tx.modify(r + 1, 0 - val); T.modify(l, val * (-l + 1)); T.modify(r + 1, val * r); //T.modify(r + 1, (val * (r - l + 1)) - (val * (-l + 1))); }
- Mishra P. A New Algorithm for Updating and Querying Sub-arrays of Multidimensional Arrays
- petr-mitrichev.blogspot.ru — Fenwick tree range updates
- topcoder.com — How to modify BIT to update the ranges and query the ranges?
- topcoder.com — Range update in BIT
- kartikkukreja.wordpress.com — Range updates with BIT / Fenwick Tree
Данный метод может быть обобщён на большие размерности: Варианты дерева Фенвика
Метод М. Кормышова
Введём массив fAdd[], i-й элемент которого хранит значение, добавляемое ко всем элементам массива a[] на отрезке F(i)..i.
int query(int r) { int res = 0; for (int i = r; i >= 0; i = (i & (i + 1)) - 1) res += f[i] + fAdd[i] * (i - (i & (i + 1)) + 1); for (int i = (r | (r + 1)); i < MAX_SIZE; i |= i + 1) res += fAdd[i] * (r - (i & (i + 1)) + 1); return res; } void modify(int r, int val) { for (int i = r; i >= 0; i = (i & (i + 1)) - 1) fAdd[i] += val; for (int i = (r | (r + 1)); i < MAX_SIZE; i |= i + 1) f[i] += val * (r - (i & (i + 1)) + 1); } int sum(int l, int r) { return query(r) - (l ? query(l - 1) : 0); } void add(int l, int r, int val) { modify(r, val); if (l) modify(l - 1, -val); }
Ссылки
Теория:
Демонстрация:
Код:
Задачи: