Use Cases of Fenwick Tree for Competitive Programming
A Fenwick tree can support the following range operations:
- Point Update and Range Query
- Range Update and Point Query
- Range Update and Range Query
In this type of query, we are required to find running values over a range [L, R] but the updates are limited to a single index. We have already covered this in the above implementation.
In this type of query, we are required to update values over a range [L, R] but the queries are limited to a single index. In this case, we perform the range updates and point queries in opposite manner as we did in Point Update and Range Query. We can update a range [L, R] with value X by making 2 point updates add(L, X) and add (R+1, -X), and if we want to query an element at index idx, we can simply take the prefix sum till idx to get our answer.
#include <bits/stdc++.h>
using namespace std;
// Method to calculate prefix sum till index idx
int sum(int idx, vector<int>& F)
{
int runningSum = 0;
// Summing up all the partial sums
while (idx > 0) {
runningSum += F[idx];
int rightMostSetBit = (idx & (-idx));
idx -= rightMostSetBit;
}
return runningSum;
}
// Method to update the array by adding X to index idx
void add(int idx, int X, vector<int>& F)
{
while (idx < F.size()) {
F[idx] += X;
int rightMostSetBit = (idx & (-idx));
idx += rightMostSetBit;
}
}
// Method to fetch element at index idx
int pointQuery(int idx, vector<int>& F)
{
return sum(idx, F);
}
// Add X to all elements in range [l, r]
void rangeUpdate(int l, int r, int X, vector<int> &F) {
add(l, X, F);
add(r + 1, -X, F);
}
int main()
{
int n = 5;
// 1 - based indexing
vector<int> arr{ -1e9, 1, 2, 3, 4, 5 };
// Initially all the values of Fenwick tree are 0
vector<int> F(n + 1, 0);
// Build the fenwick tree
for (int i = 1; i <= n; i++) {
rangeUpdate(i, i, arr[i], F);
}
// fetch the element at index 2
cout << pointQuery(2, F) << "\n";
// fetch the element at index 4
cout << pointQuery(4, F) << "\n";
// Add X to all the elements from l to r
int l = 2, r = 4;
int X = 7;
rangeUpdate(l, r, X, F);
// fetch the element at index 2
cout << pointQuery(2, F) << "\n";
// fetch the element at index 4
cout << pointQuery(4, F) << "\n";
return 0;
}
import java.util.Arrays;
public class FenwickTree {
// Method to calculate prefix sum till index idx
static int sum(int idx, int[] F) {
int runningSum = 0;
// Summing up all the partial sums
while (idx > 0) {
runningSum += F[idx];
int rightMostSetBit = (idx & -idx);
idx -= rightMostSetBit;
}
return runningSum;
}
// Method to update the array by adding X to index idx
static void add(int idx, int X, int[] F) {
while (idx < F.length) {
F[idx] += X;
int rightMostSetBit = (idx & -idx);
idx += rightMostSetBit;
}
}
// Method to fetch element at index idx
static int pointQuery(int idx, int[] F) {
return sum(idx, F);
}
// Add X to all elements in range [l, r]
static void rangeUpdate(int l, int r, int X, int[] F) {
add(l, X, F);
add(r + 1, -X, F);
}
public static void main(String[] args) {
int n = 5;
// 1 - based indexing
int[] arr = {-1_000_000_000, 1, 2, 3, 4, 5};
// Initially all the values of Fenwick tree are 0
int[] F = new int[n + 1];
// Build the fenwick tree
for (int i = 1; i <= n; i++) {
rangeUpdate(i, i, arr[i], F);
}
// fetch the element at index 2
System.out.println(pointQuery(2, F));
// fetch the element at index 4
System.out.println(pointQuery(4, F));
// Add X to all the elements from l to r
int l = 2, r = 4;
int X = 7;
rangeUpdate(l, r, X, F);
// fetch the element at index 2
System.out.println(pointQuery(2, F));
// fetch the element at index 4
System.out.println(pointQuery(4, F));
}
}
def sum(idx, F):
running_sum = 0
while idx > 0:
running_sum += F[idx]
right_most_set_bit = (idx & -idx)
idx -= right_most_set_bit
return running_sum
def add(idx, X, F):
while idx < len(F):
F[idx] += X
right_most_set_bit = (idx & -idx)
idx += right_most_set_bit
def point_query(idx, F):
return sum(idx, F)
def range_update(l, r, X, F):
# Add X to the element at index l
add(l, X, F)
# Subtract X from the element at index (r + 1)
add(r + 1, -X, F)
if __name__ == "__main__":
n = 5
arr = [-1e9, 1, 2, 3, 4, 5]
# Initially, all the values of the Fenwick tree are 0
F = [0] * (n + 1)
# Build the Fenwick tree by performing range updates
for i in range(1, n + 1):
range_update(i, i, arr[i], F)
# Fetch the element at index 2 using point query
print("Element at index 2:", point_query(2, F))
# Fetch the element at index 4 using point query
print("Element at index 4:", point_query(4, F))
# Add X to all the elements from index l to r
l, r, X = 2, 4, 7
range_update(l, r, X, F)
# Fetch the updated element at index 2
print("Updated element at index 2:", point_query(2, F))
# Fetch the updated element at index 4
print("Updated element at index 4:", point_query(4, F))
using System;
class FenwickTree
{
// Method to calculate prefix sum till index idx
static int Sum(int idx, int[] F)
{
int runningSum = 0;
// Summing up all the partial sums
while (idx > 0)
{
runningSum += F[idx];
int rightMostSetBit = (idx & (-idx));
idx -= rightMostSetBit;
}
return runningSum;
}
// Method to update the array by adding X to index idx
static void Add(int idx, int X, int[] F)
{
while (idx < F.Length)
{
F[idx] += X;
int rightMostSetBit = (idx & (-idx));
idx += rightMostSetBit;
}
}
// Method to fetch element at index idx
static int PointQuery(int idx, int[] F)
{
return Sum(idx, F);
}
// Add X to all elements in range [l, r]
static void RangeUpdate(int l, int r, int X, int[] F)
{
Add(l, X, F);
Add(r + 1, -X, F);
}
static void Main()
{
int n = 5;
// 1 - based indexing
int[] arr = { -1000000000, 1, 2, 3, 4, 5 };
// Initially all the values of Fenwick tree are 0
int[] F = new int[n + 1];
// Build the fenwick tree
for (int i = 1; i <= n; i++)
{
RangeUpdate(i, i, arr[i], F);
}
// Fetch the element at index 2
Console.WriteLine(PointQuery(2, F));
// Fetch the element at index 4
Console.WriteLine(PointQuery(4, F));
// Add X to all the elements from l to r
int l = 2, r = 4;
int X = 7;
RangeUpdate(l, r, X, F);
// Fetch the element at index 2
Console.WriteLine(PointQuery(2, F));
// Fetch the element at index 4
Console.WriteLine(PointQuery(4, F));
}
}
// Function to calculate prefix sum till index idx
function sum(idx, F) {
let runningSum = 0;
// Summing up all the partial sums
while (idx > 0) {
runningSum += F[idx];
let rightMostSetBit = (idx & -idx);
idx -= rightMostSetBit;
}
return runningSum;
}
// Function to update the array by adding X to index idx
function add(idx, X, F) {
while (idx < F.length) {
F[idx] += X;
let rightMostSetBit = (idx & -idx);
idx += rightMostSetBit;
}
}
// Function to fetch element at index idx
function pointQuery(idx, F) {
return sum(idx, F);
}
// Function to add X to all elements in range [l, r]
function rangeUpdate(l, r, X, F) {
add(l, X, F);
add(r + 1, -X, F);
}
// Main function
function main() {
let n = 5;
// 1 - based indexing
let arr = [-1_000_000_000, 1, 2, 3, 4, 5];
// Initially all the values of Fenwick tree are 0
let F = new Array(n + 1).fill(0);
// Build the Fenwick tree
for (let i = 1; i <= n; i++) {
rangeUpdate(i, i, arr[i], F);
}
// fetch the element at index 2
console.log(pointQuery(2, F));
// fetch the element at index 4
console.log(pointQuery(4, F));
// Add X to all the elements from l to r
let l = 2, r = 4;
let X = 7;
rangeUpdate(l, r, X, F);
// fetch the element at index 2
console.log(pointQuery(2, F));
// fetch the element at index 4
console.log(pointQuery(4, F));
}
// Call the main function
main();
Output
2 4 9 11
In this type of query, we are required to find running values and handle updates over range [L, R]. To handle range updates and range queries, we need to maintain two Fenwick trees, say F1[] and F2[].
3.1 Handling Range Updates:
Initially, all the elements in arr[], F1[] and F2[] are zero. Now, if we want to increment a range [L, R] by a value X then we can use the same approach as we did in Range Update and Point Query. So, we can call add(F1, L, X) and add(F1, R+1, -X). But, after this we will also call add(F2, L, X * (L – 1)) and add(F2, R, -X * R). The reason behind calling these two additional calls will be clear when we will explore about handling Range Queries. So, whenever we need to add X to a range [L, R], we will do the following:
add(F1, L, X)
add(F1, R + 1, -X)
add(F2, L, X * (L – 1))
add(F2, R, -X * R)
Now, if we try to calculate prefix sum till any index i in F1[], that is sum(F1, 0, i), we will have one of the following cases:
- If i < L, then the prior increment will have no effect on it, so sum(F1, 0, i) = 0
- If L <= i <= R, then the prior increment will add X at index i, so sum(F1, 0, i) = X
- If i > R, then the prior increment will have no effect on it, so sum(F1, 0, i) = 0
Now, if we try to calculate prefix sum till any index i in F2[], that is sum(F2, 0, i), we will have one of the following cases:
- If i < L, then the prior increment will have no effect on it, so sum(F2, 0, i) = 0
- If L <= i <= R, then the prior increment will add X * (L – 1), so sum(F2, 0, i) = X * (L – 1)
- If i > R, then the prior increment will add X * (L – 1) + (-X * R), so sum(F2, 0, i) = X * (L – 1) – (X * R)
3.2 Handling Range Queries:
Now, after incrementing the range [L, R] by a value X, we want to get the prefix sum till ith index, then we will have one of the following cases:
- If i < L, then the prior increment will have no effect on the prefix sum till i, so sum[0, i] = 0
- If L <= i <= R, then the prefix sum till ith index will be equal to X * (i – L + 1), so sum[0, i] = (X * i) – (X * (L + 1))
- If R < i, then the prefix sum till ith index will be equal to X * (R – L + 1), so sum[0, i] = (X * R) – X * (L – 1)
So, if we observe carefully, for all the above 3 cases we can say that,
sum[0, i] = (sum(F1, 0, i) * i) – sum(F2, 0, i)
This is why we made two additional calls while handing the range updates earlier as we wanted to have the additional terms to calculate the prefix sum till any index i. Since we can calculate the prefix sum till any index, we can also calculate the range sum [L, R] by subtracting prefix sum till (L-1) from prefix sum till R.
Implementation of Range Update and Range Query using Fenwick Trees:
#include <bits/stdc++.h>
using namespace std;
// Method to calculate prefix sum till index idx
int sum(int idx, vector<int>& F)
{
int runningSum = 0;
// Summing up all the partial sums
while (idx > 0) {
runningSum += F[idx];
int rightMostSetBit = (idx & (-idx));
idx -= rightMostSetBit;
}
return runningSum;
}
// Method to update the array by adding X to index idx
void add(int idx, int X, vector<int>& F)
{
while (idx < F.size()) {
F[idx] += X;
int rightMostSetBit = (idx & (-idx));
idx += rightMostSetBit;
}
}
// Method to calculate the prefix sum till ith index
int prefSum(int idx, vector<int> &F1, vector<int> &F2) {
return sum(idx, F1) * idx - sum(idx, F2);
}
// Method to calculate sum of range [L, R]
int rangeQuery(int L, int R, vector<int>& F1, vector<int> &F2) {
return prefSum(R, F1, F2) - prefSum(L - 1, F1, F2);
}
// Add X to all elements in range [l, r]
void rangeUpdate(int l, int r, int X, vector<int> &F1, vector<int> &F2) {
add(l, X, F1);
add(r + 1, -X, F1);
add(l, X * (l - 1) , F2);
add(r + 1, -(X * r), F2);
}
int main()
{
int n = 5;
// 1 - based indexing
vector<int> arr{ -1e9, 1, 2, 3, 4, 5 };
// Initially all the values of Fenwick trees are 0
vector<int> F1(n + 1, 0), F2(n + 1, 0);
// Build the fenwick tree
for (int i = 1; i <= n; i++) {
rangeUpdate(i, i, arr[i], F1, F2);
}
// Sum of elements from index 2 to 4
cout << rangeQuery(2, 4, F1, F2) << "\n";
// Sum of elements from index 1 to 5
cout << rangeQuery(1, 5, F1, F2) << "\n";
// Add X to all the elements from 2 to 4
int l = 2, r = 4;
int X = 7;
rangeUpdate(l, r, X, F1, F2);
// Sum of elements from index 2 to 4
cout << rangeQuery(2, 4, F1, F2) << "\n";
// Sum of elements from index 1 to 5
cout << rangeQuery(1, 5, F1, F2) << "\n";
return 0;
}
import java.util.*;
public class FenwickTree {
// Method to calculate prefix sum till index idx
static int sum(int idx, int[] F) {
int runningSum = 0;
// Summing up all the partial sums
while (idx > 0) {
runningSum += F[idx];
int rightMostSetBit = (idx & (-idx));
idx -= rightMostSetBit;
}
return runningSum;
}
// Method to update the array by adding X to index idx
static void add(int idx, int X, int[] F) {
while (idx < F.length) {
F[idx] += X;
int rightMostSetBit = (idx & (-idx));
idx += rightMostSetBit;
}
}
// Method to calculate the prefix sum till ith index
static int prefSum(int idx, int[] F1, int[] F2) {
return sum(idx, F1) * idx - sum(idx, F2);
}
// Method to calculate sum of range [L, R]
static int rangeQuery(int L, int R, int[] F1, int[] F2) {
return prefSum(R, F1, F2) - prefSum(L - 1, F1, F2);
}
// Add X to all elements in range [l, r]
static void rangeUpdate(int l, int r, int X, int[] F1, int[] F2) {
add(l, X, F1);
add(r + 1, -X, F1);
add(l, X * (l - 1), F2);
add(r + 1, -(X * r), F2);
}
public static void main(String[] args) {
int n = 5;
// 1 - based indexing
int[] arr = new int[]{Integer.MIN_VALUE, 1, 2, 3, 4, 5};
// Initially all the values of Fenwick trees are 0
int[] F1 = new int[n + 1];
int[] F2 = new int[n + 1];
// Build the fenwick tree
for (int i = 1; i <= n; i++) {
rangeUpdate(i, i, arr[i], F1, F2);
}
// Sum of elements from index 2 to 4
System.out.println(rangeQuery(2, 4, F1, F2));
// Sum of elements from index 1 to 5
System.out.println(rangeQuery(1, 5, F1, F2));
// Add X to all the elements from 2 to 4
int l = 2, r = 4;
int X = 7;
rangeUpdate(l, r, X, F1, F2);
// Sum of elements from index 2 to 4
System.out.println(rangeQuery(2, 4, F1, F2));
// Sum of elements from index 1 to 5
System.out.println(rangeQuery(1, 5, F1, F2));
}
}
//This code is contributed by Monu.
# Method to calculate prefix sum till index idx
def sum(idx, F):
runningSum = 0
# Summing up all the partial sums
while idx > 0:
runningSum += F[idx]
rightMostSetBit = (idx & -idx)
idx -= rightMostSetBit
return runningSum
# Method to update the array by adding X to index idx
def add(idx, X, F):
while idx < len(F):
F[idx] += X
rightMostSetBit = (idx & -idx)
idx += rightMostSetBit
# Method to calculate the prefix sum till ith index
def prefSum(idx, F1, F2):
return sum(idx, F1) * idx - sum(idx, F2)
# Method to calculate sum of range [L, R]
def rangeQuery(L, R, F1, F2):
return prefSum(R, F1, F2) - prefSum(L - 1, F1, F2)
# Add X to all elements in range [l, r]
def rangeUpdate(l, r, X, F1, F2):
add(l, X, F1)
add(r + 1, -X, F1)
add(l, X * (l - 1), F2)
add(r + 1, -(X * r), F2)
if __name__ == "__main__":
n = 5
# 1 - based indexing
arr = [-10**9, 1, 2, 3, 4, 5]
# Initially all the values of Fenwick trees are 0
F1 = [0] * (n + 1)
F2 = [0] * (n + 1)
# Build the fenwick tree
for i in range(1, n + 1):
rangeUpdate(i, i, arr[i], F1, F2)
# Sum of elements from index 2 to 4
print(rangeQuery(2, 4, F1, F2))
# Sum of elements from index 1 to 5
print(rangeQuery(1, 5, F1, F2))
# Add X to all the elements from 2 to 4
l, r = 2, 4
X = 7
rangeUpdate(l, r, X, F1, F2)
# Sum of elements from index 2 to 4
print(rangeQuery(2, 4, F1, F2))
# Sum of elements from index 1 to 5
print(rangeQuery(1, 5, F1, F2))
#this code is contributed by Monu.
using System;
using System.Collections.Generic;
public class FenwickTree {
private List<int> F1, F2;
public FenwickTree(int n) {
F1 = new List<int>(new int[n + 1]);
F2 = new List<int>(new int[n + 1]);
}
private int Sum(int idx, List<int> F) {
int runningSum = 0;
while (idx > 0) {
runningSum += F[idx];
idx -= idx & -idx;
}
return runningSum;
}
private void Add(int idx, int X, List<int> F) {
while (idx < F.Count) {
F[idx] += X;
idx += idx & -idx;
}
}
private int PrefSum(int idx, List<int> F1, List<int> F2) {
return Sum(idx, F1) * idx - Sum(idx, F2);
}
public int RangeQuery(int L, int R) {
return PrefSum(R, F1, F2) - PrefSum(L - 1, F1, F2);
}
public void RangeUpdate(int l, int r, int X) {
Add(l, X, F1);
Add(r + 1, -X, F1);
Add(l, X * (l - 1), F2);
Add(r + 1, -(X * r), F2);
}
}
class MainClass {
public static void Main(string[] args) {
int n = 5;
// 1-based indexing
List<int> arr = new List<int> { -1000000000, 1, 2, 3, 4, 5 };
FenwickTree fenwickTree = new FenwickTree(n);
// Build the Fenwick tree
for (int i = 1; i <= n; i++) {
fenwickTree.RangeUpdate(i, i, arr[i]);
}
// Sum of elements from index 2 to 4
Console.WriteLine(fenwickTree.RangeQuery(2, 4));
// Sum of elements from index 1 to 5
Console.WriteLine(fenwickTree.RangeQuery(1, 5));
// Add X to all the elements from 2 to 4
int l = 2, r = 4;
int X = 7;
fenwickTree.RangeUpdate(l, r, X);
// Sum of elements from index 2 to 4
Console.WriteLine(fenwickTree.RangeQuery(2, 4));
// Sum of elements from index 1 to 5
Console.WriteLine(fenwickTree.RangeQuery(1, 5));
}
}
//This code is contributed by monu.
// Method to calculate prefix sum till index idx
function sum(idx, F) {
let runningSum = 0;
// Summing up all the partial sums
while (idx > 0) {
runningSum += F[idx];
const rightMostSetBit = (idx & (-idx));
idx -= rightMostSetBit;
}
return runningSum;
}
// Method to update the array by adding X to index idx
function add(idx, X, F) {
while (idx < F.length) {
F[idx] += X;
const rightMostSetBit = (idx & (-idx));
idx += rightMostSetBit;
}
}
// Method to calculate the prefix sum till ith index
function prefSum(idx, F1, F2) {
return sum(idx, F1) * idx - sum(idx, F2);
}
// Method to calculate sum of range [L, R]
function rangeQuery(L, R, F1, F2) {
return prefSum(R, F1, F2) - prefSum(L - 1, F1, F2);
}
// Add X to all elements in range [l, r]
function rangeUpdate(l, r, X, F1, F2) {
add(l, X, F1);
add(r + 1, -X, F1);
add(l, X * (l - 1), F2);
add(r + 1, -(X * r), F2);
}
function main() {
const n = 5;
// 1 - based indexing
const arr = [-1e9, 1, 2, 3, 4, 5];
// Initially all the values of Fenwick trees are 0
const F1 = new Array(n + 1).fill(0);
const F2 = new Array(n + 1).fill(0);
// Build the fenwick tree
for (let i = 1; i <= n; i++) {
rangeUpdate(i, i, arr[i], F1, F2);
}
// Sum of elements from index 2 to 4
console.log(rangeQuery(2, 4, F1, F2));
// Sum of elements from index 1 to 5
console.log(rangeQuery(1, 5, F1, F2));
// Add X to all the elements from 2 to 4
const l = 2, r = 4;
const X = 7;
rangeUpdate(l, r, X, F1, F2);
// Sum of elements from index 2 to 4
console.log(rangeQuery(2, 4, F1, F2));
// Sum of elements from index 1 to 5
console.log(rangeQuery(1, 5, F1, F2));
}
main();
//This code is contributed by Monu.
Output
9 15 30 36
Fenwick Tree (Binary Indexed Tree) for Competitive Programming
In the world of competitive programming, speed is everything. Fenwick Tree (also known as Binary Indexed Tree), created by Peter M. Fenwick. They’re like secret weapons for programmers, especially when you need to quickly find cumulative frequencies in problem-solving. This article breaks down Fenwick Trees in simple terms—how they’re built and why they’re handy for competitive programming. Whether you’re starting out or a pro looking for a speed boost, Fenwick Trees could be your key to success in coding competitions. Let’s dive in!
Contact Us