Дерево Фенвіка
Нехай — деяка групова операція (бінарна асоціативна функція над множиною з нейтральним елементом та оберненими елементами), а — масив цілих чисел довжини . Позначимо інфіксний запис через ; тобто для довільних цілих . (Оскільки операція асоціативна, при інфіксному записі ми опускатимемо дужки, що задають порядок застосування .)
Дерево Фенвіка — це структура даних, яка:
- обчислює значення функції на заданому відрізку (тобто ) за часу
- оновлює значення елемента масиву за часу
- потребує пам'яті (стільки ж, скільки й сам масив )
- проста у використанні та програмуванні, особливо у випадку багатовимірних масивів
Найпоширеніше застосування дерева Фенвіка — обчислення суми на відрізку. Наприклад, якщо взяти за групову операцію додавання над множиною цілих чисел, тобто : тоді бінарна операція — це , тож .
Дерево Фенвіка також називають двійковим індексованим деревом (BIT). Уперше його описали в роботі під назвою "A new data structure for cumulative frequency tables" (Peter M. Fenwick, 1994).
- Чи є операція на відрізку оборотною (наприклад, сума за наявності віднімання), тобто утворює групу? (якщо операція лише асоціативна, як / без обернення → дерево відрізків)
- Чи потрібні оновлення елементів між запитами? (якщо масив незмінний, а потрібен лише / за → розріджена таблиця)
- Чи важлива компактність і простота коду (особливо для багатовимірних запитів) порівняно з гнучкішим, але громіздкішим деревом відрізків?
Опис
Огляд
Для простоти вважатимемо, що функція задана як над цілими числами.
Нехай нам дано масив цілих чисел . (Зауважимо, що ми використовуємо нумерацію з нуля.) Дерево Фенвіка — це просто масив , де кожен елемент дорівнює сумі елементів масиву на деякому відрізку :
де — деяка функція, що задовольняє умову . Функцію ми означимо в наступних кількох абзацах.
Цю структуру даних називають деревом, бо її можна гарно зобразити у вигляді дерева, хоча нам і не потрібно будувати справжнє дерево з вершинами та ребрами. Для обробки всіх запитів нам достатньо підтримувати лише масив .
Зауваження: Подане тут дерево Фенвіка використовує нумерацію з нуля. Багато хто користується варіантом дерева Фенвіка з нумерацією з одиниці. Тому в розділі про реалізацію ви також знайдете альтернативну реалізацію з нумерацією з одиниці. Обидві версії еквівалентні за часовою та просторовою складністю.
Тепер ми можемо записати псевдокод для двох згаданих вище операцій. Нижче ми отримуємо суму елементів масиву на відрізку та оновлюємо (збільшуємо) деякий елемент :
def sum(int r):
res = 0
while (r >= 0):
res += t[r]
r = g(r) - 1
return res
def increase(int i, int delta):
for all j with g(j) <= i <= j:
t[j] += delta
Функція sum працює так:
- Спершу вона додає до
resultсуму на відрізку (тобто ). - Потім вона «перестрибує» до відрізка і додає до
resultсуму цього відрізка. - Це триває доти, доки вона не «перестрибне» з до ; на цьому функція
sumприпиняє стрибати.
Функція increase працює за тією самою аналогією, але «стрибає» в напрямку зростання індексів:
- Сума для кожного відрізка вигляду , що задовольняє умову , збільшується на
delta; тобтоt[j] += delta. Отже, оновлюються всі елементи , що відповідають відрізкам, у яких лежить .
Складність обох функцій sum та increase залежить від функції .
Існує багато способів обрати функцію так, щоб для всіх .
Наприклад, підходить функція , що дає (у такому разі запити на суму повільні).
Можна було б також узяти функцію .
Це відповідало б масивам префіксних сум (у такому разі знаходження суми на відрізку потребуватиме лише сталого часу; проте оновлення будуть повільними).
Хитрість алгоритму дерев Фенвіка полягає в тому, що він використовує особливе означення функції , яке дозволяє виконувати обидві операції за часу.
Означення
Обчислення задається такою простою операцією: ми замінюємо всі завершальні біти у двійковому записі числа на біти .
Іншими словами, якщо наймолодший двійковий розряд числа дорівнює , то . А інакше наймолодший розряд дорівнює , і тоді ми беремо цю та всі інші завершальні одиниці й перевертаємо їх.
Наприклад, ми отримуємо
Для описаної вище нетривіальної операції існує проста реалізація через бітові операції:
де — оператор побітового І. Неважко переконатися, що це рішення робить те саме, що й описана вище операція.
Тепер нам лише потрібно знайти спосіб перебрати всі такі, що .
Легко бачити, що ми можемо знайти всі такі , починаючи з та перевертаючи останній невстановлений біт. Цю операцію ми назвемо . Наприклад, для маємо:
Як і слід було очікувати, операцію теж можна просто виконати через бітові операції:
де — оператор побітового АБО.
На наступному зображенні показано один із можливих способів інтерпретації дерева Фенвіка як дерева. Вершини дерева показують відрізки, які вони покривають.

Реалізація
Знаходження суми в одновимірному масиві
Тут ми наводимо реалізацію дерева Фенвіка для запитів на суму та оновлень одного елемента.
Звичайне дерево Фенвіка вміє відповідати лише на запити на суму вигляду за допомогою sum(int r), однак ми можемо відповідати й на інші запити вигляду , обчисливши дві суми та і віднявши їх.
Це реалізовано в методі sum(int l, int r).
Також ця реалізація підтримує два конструктори. Ви можете створити дерево Фенвіка, ініціалізоване нулями, або перетворити наявний масив на форму дерева Фенвіка.
- C++
- Python
- TypeScript
- Go
struct FenwickTree {
vector<int> bit; // двійкове індексоване дерево
int n;
FenwickTree(int n) {
this->n = n;
bit.assign(n, 0);
}
FenwickTree(vector<int> const &a) : FenwickTree(a.size()) {
for (size_t i = 0; i < a.size(); i++)
add(i, a[i]);
}
int sum(int r) {
int ret = 0;
for (; r >= 0; r = (r & (r + 1)) - 1)
ret += bit[r];
return ret;
}
int sum(int l, int r) {
return sum(r) - sum(l - 1);
}
void add(int idx, int delta) {
for (; idx < n; idx = idx | (idx + 1))
bit[idx] += delta;
}
};
class FenwickTree:
def __init__(self, n):
self.n = n
self.bit = [0] * n # двійкове індексоване дерево
@classmethod
def from_array(cls, a):
# перетворюємо наявний масив на форму дерева Фенвіка
ft = cls(len(a))
for i, x in enumerate(a):
ft.add(i, x)
return ft
def sum_prefix(self, r):
# сума на відрізку [0, r]
res = 0
while r >= 0:
res += self.bit[r]
r = (r & (r + 1)) - 1
return res
def sum_range(self, l, r):
# сума на відрізку [l, r]
return self.sum_prefix(r) - self.sum_prefix(l - 1)
def add(self, idx, delta):
while idx < self.n:
self.bit[idx] += delta
idx |= idx + 1
class FenwickTree {
private bit: number[]; // двійкове індексоване дерево
private n: number;
constructor(n: number) {
this.n = n;
this.bit = new Array(n).fill(0);
}
// перетворюємо наявний масив на форму дерева Фенвіка
static fromArray(a: number[]): FenwickTree {
const ft = new FenwickTree(a.length);
a.forEach((x, i) => ft.add(i, x));
return ft;
}
// сума на відрізку [0, r]
sumPrefix(r: number): number {
let res = 0;
for (; r >= 0; r = (r & (r + 1)) - 1) res += this.bit[r];
return res;
}
// сума на відрізку [l, r]
sumRange(l: number, r: number): number {
return this.sumPrefix(r) - this.sumPrefix(l - 1);
}
add(idx: number, delta: number): void {
for (; idx < this.n; idx = idx | (idx + 1)) this.bit[idx] += delta;
}
}
type FenwickTree struct {
bit []int // двійкове індексоване дерево
n int
}
func NewFenwickTree(n int) *FenwickTree {
return &FenwickTree{bit: make([]int, n), n: n}
}
// перетворюємо наявний масив на форму дерева Фенвіка
func NewFenwickTreeFromArray(a []int) *FenwickTree {
ft := NewFenwickTree(len(a))
for i, x := range a {
ft.Add(i, x)
}
return ft
}
// сума на відрізку [0, r]
func (ft *FenwickTree) SumPrefix(r int) int {
res := 0
for ; r >= 0; r = (r & (r + 1)) - 1 {
res += ft.bit[r]
}
return res
}
// сума на відрізку [l, r]
func (ft *FenwickTree) SumRange(l, r int) int {
return ft.SumPrefix(r) - ft.SumPrefix(l-1)
}
func (ft *FenwickTree) Add(idx, delta int) {
for ; idx < ft.n; idx = idx | (idx + 1) {
ft.bit[idx] += delta
}
}
Лінійна побудова
Наведена вище реалізація потребує часу. Її можна покращити до часу.
Ідея полягає в тому, що число за індексом робить внесок у відрізок, що зберігається в , та в усі відрізки, у які робить внесок індекс . Тож, додаючи числа по порядку, вам потрібно лише проштовхнути поточну суму далі до наступного відрізка, де вона потім буде проштовхнута далі до наступного відрізка, і так далі.
- C++
- Python
- TypeScript
- Go
FenwickTree(vector<int> const &a) : FenwickTree(a.size()){
for (int i = 0; i < n; i++) {
bit[i] += a[i];
int r = i | (i + 1);
if (r < n) bit[r] += bit[i];
}
}
@classmethod
def from_array(cls, a):
ft = cls(len(a))
for i in range(ft.n):
ft.bit[i] += a[i]
r = i | (i + 1)
if r < ft.n:
ft.bit[r] += ft.bit[i]
return ft
static fromArray(a: number[]): FenwickTree {
const ft = new FenwickTree(a.length);
for (let i = 0; i < ft.n; i++) {
ft.bit[i] += a[i];
const r = i | (i + 1);
if (r < ft.n) ft.bit[r] += ft.bit[i];
}
return ft;
}
func NewFenwickTreeFromArray(a []int) *FenwickTree {
ft := NewFenwickTree(len(a))
for i := 0; i < ft.n; i++ {
ft.bit[i] += a[i]
r := i | (i + 1)
if r < ft.n {
ft.bit[r] += ft.bit[i]
}
}
return ft
}
Знаходження мінімуму на в одновимірному масиві
Очевидно, що не існує простого способу знайти мінімум на відрізку за допомогою дерева Фенвіка, оскільки дерево Фенвіка вміє відповідати лише на запити вигляду .
Крім того, щоразу під час оновлення (update) значення нове значення має бути меншим за поточне.
Обидва ці суттєві обмеження виникають тому, що операція разом із множиною цілих чисел не утворює групи, бо немає обернених елементів.
- C++
- Python
- TypeScript
- Go
struct FenwickTreeMin {
vector<int> bit;
int n;
const int INF = (int)1e9;
FenwickTreeMin(int n) {
this->n = n;
bit.assign(n, INF);
}
FenwickTreeMin(vector<int> a) : FenwickTreeMin(a.size()) {
for (size_t i = 0; i < a.size(); i++)
update(i, a[i]);
}
int getmin(int r) {
int ret = INF;
for (; r >= 0; r = (r & (r + 1)) - 1)
ret = min(ret, bit[r]);
return ret;
}
void update(int idx, int val) {
for (; idx < n; idx = idx | (idx + 1))
bit[idx] = min(bit[idx], val);
}
};
class FenwickTreeMin:
INF = 10**9
def __init__(self, n):
self.n = n
self.bit = [self.INF] * n
@classmethod
def from_array(cls, a):
ft = cls(len(a))
for i, x in enumerate(a):
ft.update(i, x)
return ft
def getmin(self, r):
# мінімум на відрізку [0, r]
res = self.INF
while r >= 0:
res = min(res, self.bit[r])
r = (r & (r + 1)) - 1
return res
def update(self, idx, val):
# нове значення має бути меншим за поточне
while idx < self.n:
self.bit[idx] = min(self.bit[idx], val)
idx |= idx + 1
class FenwickTreeMin {
private bit: number[];
private n: number;
private static readonly INF = 1e9;
constructor(n: number) {
this.n = n;
this.bit = new Array(n).fill(FenwickTreeMin.INF);
}
static fromArray(a: number[]): FenwickTreeMin {
const ft = new FenwickTreeMin(a.length);
a.forEach((x, i) => ft.update(i, x));
return ft;
}
// мінімум на відрізку [0, r]
getmin(r: number): number {
let res = FenwickTreeMin.INF;
for (; r >= 0; r = (r & (r + 1)) - 1) res = Math.min(res, this.bit[r]);
return res;
}
// нове значення має бути меншим за поточне
update(idx: number, val: number): void {
for (; idx < this.n; idx = idx | (idx + 1))
this.bit[idx] = Math.min(this.bit[idx], val);
}
}
const inf = int(1e9)
type FenwickTreeMin struct {
bit []int
n int
}
func NewFenwickTreeMin(n int) *FenwickTreeMin {
bit := make([]int, n)
for i := range bit {
bit[i] = inf
}
return &FenwickTreeMin{bit: bit, n: n}
}
func NewFenwickTreeMinFromArray(a []int) *FenwickTreeMin {
ft := NewFenwickTreeMin(len(a))
for i, x := range a {
ft.Update(i, x)
}
return ft
}
// мінімум на відрізку [0, r]
func (ft *FenwickTreeMin) GetMin(r int) int {
res := inf
for ; r >= 0; r = (r & (r + 1)) - 1 {
res = min(res, ft.bit[r])
}
return res
}
// нове значення має бути меншим за поточне
func (ft *FenwickTreeMin) Update(idx, val int) {
for ; idx < ft.n; idx = idx | (idx + 1) {
ft.bit[idx] = min(ft.bit[idx], val)
}
}
Зауваження: можна реалізувати дерево Фенвіка, яке вміє обробляти довільні запити на мінімум на відрізку та довільні оновлення. У роботі Efficient Range Minimum Queries using Binary Indexed Trees описано такий підхід. Однак за цього підходу вам потрібно підтримувати над даними друге двійкове індексоване дерево зі трохи іншою структурою, оскільки одного дерева недостатньо, щоб зберегти значення всіх елементів масиву. Реалізація також значно складніша порівняно зі звичайною реалізацією для сум.
Знаходження суми у двовимірному масиві
Як уже зазначалося, дерево Фенвіка дуже легко реалізувати для багатовимірного масиву.
- C++
- Python
- TypeScript
- Go
struct FenwickTree2D {
vector<vector<int>> bit;
int n, m;
// init(...) { ... }
int sum(int x, int y) {
int ret = 0;
for (int i = x; i >= 0; i = (i & (i + 1)) - 1)
for (int j = y; j >= 0; j = (j & (j + 1)) - 1)
ret += bit[i][j];
return ret;
}
void add(int x, int y, int delta) {
for (int i = x; i < n; i = i | (i + 1))
for (int j = y; j < m; j = j | (j + 1))
bit[i][j] += delta;
}
};
class FenwickTree2D:
def __init__(self, n, m):
self.n = n
self.m = m
self.bit = [[0] * m for _ in range(n)]
def sum(self, x, y):
res = 0
i = x
while i >= 0:
j = y
while j >= 0:
res += self.bit[i][j]
j = (j & (j + 1)) - 1
i = (i & (i + 1)) - 1
return res
def add(self, x, y, delta):
i = x
while i < self.n:
j = y
while j < self.m:
self.bit[i][j] += delta
j |= j + 1
i |= i + 1
class FenwickTree2D {
private bit: number[][];
private n: number;
private m: number;
constructor(n: number, m: number) {
this.n = n;
this.m = m;
this.bit = Array.from({ length: n }, () => new Array(m).fill(0));
}
sum(x: number, y: number): number {
let res = 0;
for (let i = x; i >= 0; i = (i & (i + 1)) - 1)
for (let j = y; j >= 0; j = (j & (j + 1)) - 1) res += this.bit[i][j];
return res;
}
add(x: number, y: number, delta: number): void {
for (let i = x; i < this.n; i = i | (i + 1))
for (let j = y; j < this.m; j = j | (j + 1)) this.bit[i][j] += delta;
}
}
type FenwickTree2D struct {
bit [][]int
n, m int
}
func NewFenwickTree2D(n, m int) *FenwickTree2D {
bit := make([][]int, n)
for i := range bit {
bit[i] = make([]int, m)
}
return &FenwickTree2D{bit: bit, n: n, m: m}
}
func (ft *FenwickTree2D) Sum(x, y int) int {
res := 0
for i := x; i >= 0; i = (i & (i + 1)) - 1 {
for j := y; j >= 0; j = (j & (j + 1)) - 1 {
res += ft.bit[i][j]
}
}
return res
}
func (ft *FenwickTree2D) Add(x, y, delta int) {
for i := x; i < ft.n; i = i | (i + 1) {
for j := y; j < ft.m; j = j | (j + 1) {
ft.bit[i][j] += delta
}
}
}
Підхід із нумерацією з одиниці
Для цього підходу ми трохи змінюємо вимоги та означення для і . Ми хочемо, щоб зберігало суму на . Це трохи змінює реалізацію та дозволяє аналогічно гарно означити :
def sum(int r):
res = 0
while (r > 0):
res += t[r]
r = g(r)
return res
def increase(int i, int delta):
for all j with g(j) < i <= j:
t[j] += delta
Обчислення задається так: скидання останнього встановленого біта у двійковому записі числа .
Останній встановлений біт можна виділити за допомогою , тож операцію можна виразити так:
І неважко бачити, що для оновлення вам потрібно змінити всі значення у послідовності , де означено як:
Як бачите, головна перевага цього підходу полягає в тому, що бітові операції дуже гарно доповнюють одна одну.
Наведену нижче реалізацію можна використовувати так само, як і інші реалізації, проте всередині вона використовує нумерацію з одиниці.
- C++
- Python
- TypeScript
- Go
struct FenwickTreeOneBasedIndexing {
vector<int> bit; // двійкове індексоване дерево
int n;
FenwickTreeOneBasedIndexing(int n) {
this->n = n + 1;
bit.assign(n + 1, 0);
}
FenwickTreeOneBasedIndexing(vector<int> a)
: FenwickTreeOneBasedIndexing(a.size()) {
for (size_t i = 0; i < a.size(); i++)
add(i, a[i]);
}
int sum(int idx) {
int ret = 0;
for (++idx; idx > 0; idx -= idx & -idx)
ret += bit[idx];
return ret;
}
int sum(int l, int r) {
return sum(r) - sum(l - 1);
}
void add(int idx, int delta) {
for (++idx; idx < n; idx += idx & -idx)
bit[idx] += delta;
}
};
class FenwickTreeOneBasedIndexing:
def __init__(self, n):
self.n = n + 1
self.bit = [0] * (n + 1) # двійкове індексоване дерево
@classmethod
def from_array(cls, a):
ft = cls(len(a))
for i, x in enumerate(a):
ft.add(i, x)
return ft
def sum_prefix(self, idx):
res = 0
idx += 1
while idx > 0:
res += self.bit[idx]
idx -= idx & -idx
return res
def sum_range(self, l, r):
return self.sum_prefix(r) - self.sum_prefix(l - 1)
def add(self, idx, delta):
idx += 1
while idx < self.n:
self.bit[idx] += delta
idx += idx & -idx
class FenwickTreeOneBasedIndexing {
private bit: number[]; // двійкове індексоване дерево
private n: number;
constructor(n: number) {
this.n = n + 1;
this.bit = new Array(n + 1).fill(0);
}
static fromArray(a: number[]): FenwickTreeOneBasedIndexing {
const ft = new FenwickTreeOneBasedIndexing(a.length);
a.forEach((x, i) => ft.add(i, x));
return ft;
}
sumPrefix(idx: number): number {
let res = 0;
for (++idx; idx > 0; idx -= idx & -idx) res += this.bit[idx];
return res;
}
sumRange(l: number, r: number): number {
return this.sumPrefix(r) - this.sumPrefix(l - 1);
}
add(idx: number, delta: number): void {
for (++idx; idx < this.n; idx += idx & -idx) this.bit[idx] += delta;
}
}
type FenwickTreeOneBased struct {
bit []int // двійкове індексоване дерево
n int
}
func NewFenwickTreeOneBased(n int) *FenwickTreeOneBased {
return &FenwickTreeOneBased{bit: make([]int, n+1), n: n + 1}
}
func NewFenwickTreeOneBasedFromArray(a []int) *FenwickTreeOneBased {
ft := NewFenwickTreeOneBased(len(a))
for i, x := range a {
ft.Add(i, x)
}
return ft
}
func (ft *FenwickTreeOneBased) SumPrefix(idx int) int {
res := 0
for idx++; idx > 0; idx -= idx & -idx {
res += ft.bit[idx]
}
return res
}
func (ft *FenwickTreeOneBased) SumRange(l, r int) int {
return ft.SumPrefix(r) - ft.SumPrefix(l-1)
}
func (ft *FenwickTreeOneBased) Add(idx, delta int) {
for idx++; idx < ft.n; idx += idx & -idx {
ft.bit[idx] += delta
}
}
Операції на відрізку
Дерево Фенвіка може підтримувати такі операції на відрізку:
- Оновлення в точці та запит на відрізку
- Оновлення на відрізку та запит у точці
- Оновлення на відрізку та запит на відрізку
1. Оновлення в точці та запит на відрізку
Це просто звичайне дерево Фенвіка, описане вище.
2. Оновлення на відрізку та запит у точці
За допомогою простих прийомів ми можемо виконувати й обернені операції: збільшувати значення на відрізках та запитувати окремі значення.
Нехай дерево Фенвіка ініціалізоване нулями.
Припустимо, що ми хочемо збільшити відрізок на .
Ми виконуємо дві операції оновлення в точці над деревом Фенвіка, а саме add(l, x) та add(r+1, -x).
Якщо ми хочемо отримати значення , нам просто потрібно взяти префіксну суму звичайним методом суми на відрізку. Щоб зрозуміти, чому це справді так, знову зосередимося на попередній операції збільшення. Якщо , то обидві операції оновлення не впливають на запит, і ми отримуємо суму . Якщо , то ми отримуємо відповідь завдяки першій операції оновлення. А якщо , то друга операція оновлення скасує вплив першої.
Наведена нижче реалізація використовує нумерацію з одиниці.
- C++
- Python
- TypeScript
- Go
void add(int idx, int val) {
for (++idx; idx < n; idx += idx & -idx)
bit[idx] += val;
}
void range_add(int l, int r, int val) {
add(l, val);
add(r + 1, -val);
}
int point_query(int idx) {
int ret = 0;
for (++idx; idx > 0; idx -= idx & -idx)
ret += bit[idx];
return ret;
}
def add(self, idx, val):
idx += 1
while idx < self.n:
self.bit[idx] += val
idx += idx & -idx
def range_add(self, l, r, val):
self.add(l, val)
self.add(r + 1, -val)
def point_query(self, idx):
res = 0
idx += 1
while idx > 0:
res += self.bit[idx]
idx -= idx & -idx
return res
add(idx: number, val: number): void {
for (++idx; idx < this.n; idx += idx & -idx) this.bit[idx] += val;
}
rangeAdd(l: number, r: number, val: number): void {
this.add(l, val);
this.add(r + 1, -val);
}
pointQuery(idx: number): number {
let res = 0;
for (++idx; idx > 0; idx -= idx & -idx) res += this.bit[idx];
return res;
}
func (ft *FenwickTree) Add(idx, val int) {
for idx++; idx < ft.n; idx += idx & -idx {
ft.bit[idx] += val
}
}
func (ft *FenwickTree) RangeAdd(l, r, val int) {
ft.Add(l, val)
ft.Add(r+1, -val)
}
func (ft *FenwickTree) PointQuery(idx int) int {
res := 0
for idx++; idx > 0; idx -= idx & -idx {
res += ft.bit[idx]
}
return res
}
Зауваження: звісно, можна також збільшити окрему точку за допомогою range_add(i, i, val).
3. Оновлення на відрізку та запит на відрізку
Щоб підтримувати і оновлення на відрізку, і запити на відрізку, ми використаємо два BIT, а саме та , ініціалізовані нулями.
Припустимо, що ми хочемо збільшити відрізок на значення .
Аналогічно до попереднього методу, ми виконуємо два оновлення в точці над : add(B1, l, x) та add(B1, r+1, -x).
А також оновлюємо . Подробиці будуть пояснені пізніше.
def range_add(l, r, x):
add(B1, l, x)
add(B1, r+1, -x)
add(B2, l, x*(l-1))
add(B2, r+1, -x*r))
Після оновлення на відрізку запит на суму на відрізку має повертати такі значення:
Ми можемо записати суму на відрізку як різницю двох доданків, де для першого доданка використовуємо , а для другого — . Різниця запитів дасть нам префіксну суму на .
Останній вираз точно дорівнює потрібним доданкам. Отже, ми можемо використовувати для відсікання зайвих доданків, коли множимо .
Ми можемо знаходити довільні суми на відрізку, обчисливши префіксні суми для та і знову взявши їхню різницю.
def add(b, idx, x):
while idx <= N:
b[idx] += x
idx += idx & -idx
def range_add(l,r,x):
add(B1, l, x)
add(B1, r+1, -x)
add(B2, l, x*(l-1))
add(B2, r+1, -x*r)
def sum(b, idx):
total = 0
while idx > 0:
total += b[idx]
idx -= idx & -idx
return total
def prefix_sum(idx):
return sum(B1, idx)*idx - sum(B2, idx)
def range_sum(l, r):
return prefix_sum(r) - prefix_sum(l-1)
Задачі для практики
- UVA 12086 - Potentiometers
- LOJ 1112 - Curious Robin Hood
- LOJ 1266 - Points in Rectangle
- Codechef - SPREAD
- SPOJ - CTRICK
- SPOJ - MATSUM
- SPOJ - DQUERY
- SPOJ - NKTEAM
- SPOJ - YODANESS
- SRM 310 - FloatingMedian
- SPOJ - Ada and Behives
- Hackerearth - Counting in Byteland
- DevSkill - Shan and String (archived)
- Codeforces - Little Artem and Time Machine
- Codeforces - Hanoi Factory
- SPOJ - Tulip and Numbers
- SPOJ - SUMSUM
- SPOJ - Sabir and Gifts
- SPOJ - The Permutation Game Again
- SPOJ - Zig when you Zag
- SPOJ - Cryon
- SPOJ - Weird Points
- SPOJ - Its a Murder
- SPOJ - Bored of Suffixes and Prefixes
- SPOJ - Mega Inversions
- Codeforces - Subsequences
- Codeforces - Ball
- GYM - The Kamphaeng Phet's Chedis
- Codeforces - Garlands
- Codeforces - Inversions after Shuffle
- GYM - Cairo Market
- Codeforces - Goodbye Souvenir
- SPOJ - Ada and Species
- Codeforces - Thor
- CSES - Forest Queries II
- Latin American Regionals 2017 - Fundraising