Implementation of Dynamic Segment Trees with Poly Hash Tables
Last Updated :
13 Apr, 2024
Dynamic Segment Trees with Poly Hash Tables is a data structure that combines the benefits of both dynamic segment trees and hash tables to efficiently handle range queries on an array of elements.
To understand this topic, let’s start with an example. Consider an array of N elements {1, 2, 3, …, N}. We want to support two operations on this array:
- Query(l, r): Return the sum of all the elements in the array between indices l and r.
- Update(i, x): Replace the value of the element at index i with x.
We can use a segment tree to support these operations efficiently in O(log N) time. However, the segment tree requires us to know the size of the array in advance and does not allow us to insert or delete elements from the array. This is where dynamic segment trees come in. A dynamic segment tree is a data structure that can handle insertions and deletions in addition to the query and update operations.
Now let’s consider the problem of adding hash tables to dynamic segment trees. A hash table is a data structure that allows us to store and retrieve values using keys in constant time. We can use hash tables to efficiently handle range queries that involve non-numeric values, such as strings or objects.
Poly Hash Tables:
The poly hash table is a type of hash table that uses a polynomial hash function to map keys to indices in the table. The polynomial hash function takes the form of
h(k) = (a_0 + a_1k + a_2k^2 + … + a_n*k^n) % p, where k is the key, a_i are coefficients, and p is a large prime number. The polynomial hash function has the property that it produces a unique index for each key with high probability, making it a good choice for hash tables.
To implement a dynamic segment tree with poly hash tables, we can use the following steps:
- Divide the array into segments of fixed length. For example, we can divide the array into segments of length sqrt(N).
- Create a dynamic segment tree that stores the sum of elements in each segment.
- For each segment, create a poly hash table that maps elements in the segment to their indices in the segment.
- When we insert or delete an element in the array, update the corresponding segment in the dynamic segment tree and the corresponding key-value pair in the poly hash table.
To handle range queries, split the query range into segments and compute the sum of segments using the dynamic segment tree. For each segment, look up the corresponding keys in the poly hash table and add up their values to get the final result.
Let’s see an example implementation of this algorithm in C++:
C++
// C++ code for the above approach
#include <bits/stdc++.h>
using namespace std;
// This class is used to compute the hash
// of a polynomial with coefficients given
// by the vector a. The hash is
// computed modulo p.
class PolyHash {
public:
PolyHash(const vector<int>& a, int p)
: a(a), p(p)
{
}
// Computes the hash of the
// polynomial at the point k.
int hash(int k) const
{
int result = 0;
for (int i = 0; i < a.size(); i++) {
result = (result + a[i] * k) % p;
}
return result;
}
private:
vector<int> a;
// Coefficients of the polynomial.
int p;
// Modulus used to compute the hash.
};
// This class implements a dynamic
// segment tree with polyhash.
class DynamicSegmentTreeWithPolyHash {
public:
DynamicSegmentTreeWithPolyHash(const vector<int>& a)
: a(a)
{
// The segment size is chosen to
// be sqrt(n), where n
// is the size of a.
segmentSize = sqrt(a.size());
// We allocate enough memory for
// 2*n nodes in the segment tree.
segmentTree.resize(2 * a.size());
// We create a vector of
// unordered_maps, one for
// each segment of size sqrt(n).
for (int i = 0; i < a.size(); i += segmentSize) {
unordered_map<int, int> polyHashTable;
// We compute the hash of the
// subarray [i, i+segmentSize]
// and store it in
// polyHashTable for each
// element in that subarray.
for (int j = i;
j < min(i + segmentSize, (int)a.size());
j++) {
polyHashTable[a[j]] = j - i;
segmentTree[a.size() + i / segmentSize]
+= a[j];
}
// We store the hash table for
// this segment in polyHashTables.
polyHashTables.push_back(polyHashTable);
}
}
// Returns the sum of elements in
// the range [l, r] of the array a.
int query(int l, int r) const
{
int sum = 0;
// We loop over the elements in
// the range [l, r] and add
// them to sum.
for (int i = l; i <= r;) {
// If we are at the beginning
// of a segment, and the segment
// contains the entire range
// [i, r], we can add the
// precomputed sum of the
// segment to sum and skip
// to the next segment.
if (i % segmentSize == 0
&& i + segmentSize - 1 < r) {
sum += segmentTree[a.size()
+ i / segmentSize];
i += segmentSize;
}
// Otherwise, we simply add
// the current element to sum
// and move to the next element.
else {
sum += a[i];
i++;
}
}
return sum;
}
// Updates the value of a[i] to x.
void update(int i, int x)
{
// We first find the segment
// that contains a[i].
int segmentIndex = i / segmentSize;
int segmentStart = segmentIndex * segmentSize;
int segmentEnd = min(segmentStart + segmentSize,
(int)a.size());
// We update the value of a[i] in
// the array and update the sum of
// the segment in the segment tree.
segmentTree[a.size() + +segmentIndex] += x - a[i];
// Update the corresponding node
// of the segment tree with the
// difference between the new and
// old values of the element.
polyHashTables[segmentIndex].erase(a[i]);
polyHashTables[segmentIndex][x] = i - segmentStart;
a[i] = x;
}
void insert(int i, int x)
// In the insert method, a new
// element x is inserted at index i
// of the vector a, and then the
// rebuild method is called to
// rebuild the segment tree
// using the updated vector.
{
a.insert(a.begin() + i, x);
rebuild();
}
void remove(int i)
// In the remove method, the element
// at index i of the vector a is
// removed, and then the rebuild method
// is called to rebuild the segment
// tree using the updated vector.
{
a.erase(a.begin() + i);
rebuild();
}
private:
void rebuild()
{
int n = a.size();
// The rebuild method is called in
// both insert and remove methods
// to rebuild the segment tree
// using the updated vector.
segmentSize = sqrt(n);
segmentTree.clear();
// It first calculates the new
// segmentSize using the
// updated size of the vector.
segmentTree.resize(2 * n);
polyHashTables.clear();
// Then it clears the segmentTree
// and polyHashTables vectors and
// rebuilds them by iterating
// through the new vector in
// segments of size segmentSize.
for (int i = 0; i < n; i += segmentSize) {
unordered_map<int, int> polyHashTable;
for (int j = i; j < min(i + segmentSize, n);
j++) {
polyHashTable[a[j]] = j - i;
// In each segment, it
// creates a new polyHashTable
// and adds the values to it,
// and calculates the sum of
// the segment and adds
// it to the segmentTree.
segmentTree[n + i / segmentSize] += a[j];
}
polyHashTables.push_back(polyHashTable);
// Finally, the polyHashTable is
// added to the polyHashTables
// vector.
}
}
private:
vector<int> a;
int segmentSize;
vector<int> segmentTree;
vector<unordered_map<int, int> > polyHashTables;
};
// Driver's code
int main()
{
vector<int> a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Initializes a vector a with 10
// integers from 1 to 10.
DynamicSegmentTreeWithPolyHash dst(a);
// It creates an instance of the
// DynamicSegmentTreeWithPolyHash
// class with the vector a.
cout << "Query(0, 4) = " << dst.query(0, 4) << endl;
// It calls the query method of the
// dst object with arguments (0, 4)
// and prints the result. expected
// output: 15
dst.update(3, 100);
// It calls the update method of the
// dst object with arguments (3, 100).
cout << "Query(2, 5) = " << dst.query(2, 5) << endl;
// It calls the query method of the
// dst object with arguments (2, 5)
// and prints the result. This should
// output "Query(2, 5) = 114".
// expected output: 114
return 0;
}
Java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
// This class is used to compute the hash of a polynomial with coefficients given by the vector a.
static class PolyHash {
private final List<Integer> a;
private final int p;
public PolyHash(List<Integer> a, int p) {
this.a = a;
this.p = p;
}
// Computes the hash of the polynomial at point k.
public int hash(int k) {
int result = 0;
for (int i = 0; i < a.size(); i++) {
result = (result + a.get(i) * k) % p;
}
return result;
}
}
// This class implements a dynamic segment tree with the polyhash.
static class GFG {
private final List<Integer> a;
private final int segmentSize;
private final List<Integer> segmentTree;
private final List<Map<Integer, Integer>> polyHashTables;
public GFG(List<Integer> a) {
this.a = a;
this.segmentSize = (int) Math.sqrt(a.size());
this.segmentTree = new ArrayList<>();
this.polyHashTables = new ArrayList<>();
for (int i = 0; i < a.size(); i += segmentSize) {
Map<Integer, Integer> polyHashTable = new HashMap<>();
int sum = 0;
for (int j = i; j < Math.min(i + segmentSize, a.size()); j++) {
polyHashTable.put(a.get(j), j - i);
sum += a.get(j);
}
segmentTree.add(sum);
polyHashTables.add(polyHashTable);
}
}
// Returns the sum of elements in the range [l, r] of array a.
public int query(int l, int r) {
int sum = 0;
for (int i = l; i <= r; i++) {
if (i % segmentSize == 0 && i + segmentSize - 1 <= r) {
sum += segmentTree.get(i / segmentSize);
i += segmentSize - 1;
} else {
sum += a.get(i);
}
}
return sum;
}
// Updates the value of a[i] to x.
public void update(int i, int x) {
int segmentIndex = i / segmentSize;
int segmentStart = segmentIndex * segmentSize;
int segmentEnd = Math.min(segmentStart + segmentSize, a.size());
segmentTree.set(segmentIndex, segmentTree.get(segmentIndex) + x - a.get(i));
polyHashTables.get(segmentIndex).remove(a.get(i));
polyHashTables.get(segmentIndex).put(x, i - segmentStart);
a.set(i, x);
}
// Inserts a new element x at index i.
public void insert(int i, int x) {
a.add(i, x);
rebuild();
}
// Removes the element at index i.
public void remove(int i) {
a.remove(i);
rebuild();
}
// Rebuilds the segment tree and polyhash tables.
private void rebuild() {
segmentTree.clear();
polyHashTables.clear();
for (int i = 0; i < a.size(); i += segmentSize) {
Map<Integer, Integer> polyHashTable = new HashMap<>();
int sum = 0;
for (int j = i; j < Math.min(i + segmentSize, a.size()); j++) {
polyHashTable.put(a.get(j), j - i);
sum += a.get(j);
}
segmentTree.add(sum);
polyHashTables.add(polyHashTable);
}
}
}
// Main method
public static void main(String[] args) {
List<Integer> a = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
a.add(i);
}
GFG dst = new GFG(a);
System.out.println("Query(0, 4) = " + dst.query(0, 4));
dst.update(3, 100);
System.out.println("Query(2, 5) = " + dst.query(2, 5));
}
}
Python3
import math
class PolyHash:
"""
This class computes the hash of a polynomial with coefficients given
by the vector a. The hash is computed modulo p.
"""
def __init__(self, a: list, p: int):
"""
Initializes the PolyHash object.
Args:
a: The coefficients of the polynomial.
p: The modulus used to compute the hash.
"""
self.a = a
self.p = p
def hash(self, k: int) -> int:
"""
Computes the hash of the polynomial at the point k.
Args:
k: The point at which to compute the hash.
Returns:
The hash of the polynomial at the point k.
"""
result = 0
for i in range(len(self.a)):
result = (result + self.a[i] * k) % self.p
return result
class DynamicSegmentTreeWithPolyHash:
"""
This class implements a dynamic segment tree with polyhash.
"""
def __init__(self, a: list):
"""
Initializes the DynamicSegmentTreeWithPolyHash object.
Args:
a: The array to build the segment tree on.
"""
self.a = a
self.segment_size = math.sqrt(len(a))
self.segment_tree = [0] * (2 * len(a))
self.poly_hash_tables = []
for i in range(0, len(a), int(self.segment_size)):
poly_hash_table = {}
for j in range(i, min(i + int(self.segment_size), len(a))):
poly_hash_table[a[j]] = j - i
self.segment_tree[len(a) + i // int(self.segment_size)] += a[j]
self.poly_hash_tables.append(poly_hash_table)
def query(self, l: int, r: int) -> int:
"""
Returns the sum of elements in the range [l, r] of the array a.
Args:
l: The left index of the range.
r: The right index of the range.
Returns:
The sum of elements in the range [l, r] of the array a.
"""
sum = 0
i = l
while i <= r:
if i % self.segment_size == 0 and i + self.segment_size - 1 < r:
sum += self.segment_tree[len(self.a) +
i // int(self.segment_size)]
i += int(self.segment_size)
else:
sum += self.a[i]
i += 1
return sum
def update(self, i: int, x: int):
"""
Updates the value of a[i] to x.
Args:
i: The index of the element to update.
x: The new value of the element.
"""
segment_index = i // int(self.segment_size)
segment_start = segment_index * int(self.segment_size)
segment_end = min(segment_start + int(self.segment_size), len(self.a))
self.segment_tree[len(self.a) + segment_index] += x - self.a[i]
self.poly_hash_tables[segment_index].pop(self.a[i])
self.poly_hash_tables[segment_index][x] = i - segment_start
self.a[i] = x
def insert(self, i: int, x: int):
"""
Inserts a new element x at index i of the vector a.
Args:
i: The index at which to insert the new element.
x: The new element to insert.
"""
self.a.insert(i, x)
self.rebuild()
def remove(self, i: int):
"""
Removes the element at index i of the vector a.
Args:
i: The index of the element to remove.
"""
self.a.pop(i)
self.rebuild()
def rebuild(self):
"""
Rebuilds the segment tree using the updated vector.
"""
self.segment_size = math.sqrt(len(self.a))
self.segment_tree = [0] * (2 * len(self.a))
self.poly_hash_tables = []
for i in range(0, len(self.a), int(self.segment_size)):
poly_hash_table = {}
for j in range(i, min(i + int(self.segment_size), len(self.a))):
poly_hash_table[self.a[j]] = j - i
self.segment_tree[len(self.a) + i //
int(self.segment_size)] += self.a[j]
self.poly_hash_tables.append(poly_hash_table)
# Driver's code
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
dst = DynamicSegmentTreeWithPolyHash(a)
print("Query(0, 4) =", dst.query(0, 4))
dst.update(3, 100)
print("Query(2, 5) =", dst.query(2, 5))
C#
using System;
using System.Collections.Generic;
public class PolyHash
{
private readonly List<int> a;
private readonly int p; // Modulus used to compute the hash
// Constructor to initialize the polynomial coefficients and modulus
public PolyHash(List<int> a, int p)
{
this.a = a;
this.p = p;
}
// Method to compute the hash of the polynomial at a given point k
public int Hash(int k)
{
int result = 0;
for (int i = 0; i < a.Count; i++)
{
// Polynomial hash calculation
result = (result + a[i] * k) % p;
}
return result;
}
}
public class DynamicSegmentTreeWithPolyHash
{
private readonly List<int> a; // Input array
private int segmentSize; // Size of each segment
private List<int> segmentTree; // Segment tree to store sums
private List<Dictionary<int, int>> polyHashTables; // Hash tables for each segment
// Constructor to initialize the dynamic segment tree with polynomial hash
public DynamicSegmentTreeWithPolyHash(List<int> a)
{
this.a = a;
segmentSize = (int)Math.Sqrt(a.Count);
segmentTree = new List<int>(2 * a.Count); // Initialize segment tree
polyHashTables = new List<Dictionary<int, int>>(); // Initialize hash tables
for (int i = 0; i < 2 * a.Count; i++)
{
segmentTree.Add(0); // Initialize segment tree nodes
}
// Divide the input array into segments and compute hash tables for each segment
for (int i = 0; i < a.Count; i += segmentSize)
{
var polyHashTable = new Dictionary<int, int>(); // Create hash table for current segment
for (int j = i; j < Math.Min(i + segmentSize, a.Count); j++)
{
polyHashTable[a[j]] = j - i; // Store element index relative to segment start
segmentTree[a.Count + i / segmentSize] += a[j]; // Update segment tree with element sum
}
polyHashTables.Add(polyHashTable); // Store hash table for current segment
}
}
// Method to query the sum of elements in a range [l, r] of the input array
public int Query(int l, int r)
{
int sum = 0;
// Loop through the range [l, r] and accumulate the sum
for (int i = l; i <= r;)
{
if (i % segmentSize == 0 && i + segmentSize - 1 < r)
{
sum += segmentTree[a.Count + i / segmentSize]; // Add precomputed segment sum
i += segmentSize; // Move to next segment
}
else
{
sum += a[i]; // Add individual element
i++; // Move to next element
}
}
return sum; // Return the sum
}
// Method to update the value of an element at index i in the input array
public void Update(int i, int x)
{
int segmentIndex = i / segmentSize; // Get the segment index containing the element
int segmentStart = segmentIndex * segmentSize; // Get the start index of the segment
// int segmentEnd = Math.Min(segmentStart + segmentSize, a.Count); // Get the end index of the segment
segmentTree[a.Count + segmentIndex] += x - a[i]; // Update segment tree with new element value
// Update the hash table of the segment with the new element value
polyHashTables[segmentIndex].Remove(a[i]); // Remove old element from hash table
polyHashTables[segmentIndex][x] = i - segmentStart; // Add new element to hash table
a[i] = x; // Update element value in input array
}
// Method to insert a new element x at index i in the input array
public void Insert(int i, int x)
{
a.Insert(i, x); // Insert new element into input array
Rebuild(); // Rebuild the segment tree and hash tables
}
// Method to remove an element at index i from the input array
public void Remove(int i)
{
a.RemoveAt(i); // Remove element from input array
Rebuild(); // Rebuild the segment tree and hash tables
}
// Method to rebuild the segment tree and hash tables based on the current input array
private void Rebuild()
{
int n = a.Count; // Get the size of the input array
segmentSize = (int)Math.Sqrt(n); // Calculate the new segment size
segmentTree = new List<int>(2 * n); // Initialize segment tree
polyHashTables.Clear(); // Clear existing hash tables
for (int i = 0; i < 2 * n; i++)
{
segmentTree.Add(0); // Initialize segment tree nodes
}
// Divide the input array into segments and compute hash tables for each segment
for (int i = 0; i < n; i += segmentSize)
{
var polyHashTable = new Dictionary<int, int>(); // Create hash table for current segment
for (int j = i; j < Math.Min(i + segmentSize, n); j++)
{
polyHashTable[a[j]] = j - i; // Store element index relative to segment start
segmentTree[n + i / segmentSize] += a[j]; // Update segment tree with element sum
}
polyHashTables.Add(polyHashTable); // Store hash table for current segment
}
}
}
// Main class to test the DynamicSegmentTreeWithPolyHash implementation
public class MainClass
{
public static void Main(string[] args)
{
List<int> a = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // Input array
// Initialize a dynamic segment tree with polynomial hash using the input array
DynamicSegmentTreeWithPolyHash dst = new DynamicSegmentTreeWithPolyHash(a);
// Query the sum of elements in the range [0, 4] and print the result
Console.WriteLine("Query(0, 4) = " + dst.Query(0, 4));
// Update the value of element at index 3 to 100
dst.Update(3, 100);
// Query the sum of elements in the range [2, 5] after update and print the result
Console.WriteLine("Query(2, 5) = " + dst.Query(2, 5));
}
}
JavaScript
// Class to compute the hash of a polynomial with coefficients given by the vector a
class PolyHash {
constructor(a, p) {
this.a = a;
this.p = p;
}
// Computes the hash of the polynomial at point k
hash(k) {
let result = 0;
for (let i = 0; i < this.a.length; i++) {
result = (result + this.a[i] * k) % this.p;
}
return result;
}
}
// Class implementing a dynamic segment tree with polyhash
class GFG {
constructor(a) {
this.a = a;
this.segmentSize = Math.sqrt(a.length);
this.segmentTree = [];
this.polyHashTables = [];
for (let i = 0; i < a.length; i += this.segmentSize) {
const polyHashTable = new Map();
let sum = 0;
for (let j = i; j < Math.min(i + this.segmentSize, a.length); j++) {
polyHashTable.set(a[j], j - i);
sum += a[j];
}
this.segmentTree.push(sum);
this.polyHashTables.push(polyHashTable);
}
}
// Returns the sum of elements in the range [l, r] of array a
query(l, r) {
let sum = 0;
for (let i = l; i <= r; i++) {
if (i % this.segmentSize === 0 && i + this.segmentSize - 1 <= r) {
sum += this.segmentTree[i / this.segmentSize];
i += this.segmentSize - 1;
} else {
sum += this.a[i];
}
}
return sum;
}
// Updates the value of a[i] to x
update(i, x) {
const segmentIndex = Math.floor(i / this.segmentSize);
const segmentStart = segmentIndex * this.segmentSize;
const segmentEnd = Math.min(segmentStart + this.segmentSize, this.a.length);
this.segmentTree[segmentIndex] += x - this.a[i];
this.polyHashTables[segmentIndex].delete(this.a[i]);
this.polyHashTables[segmentIndex].set(x, i - segmentStart);
this.a[i] = x;
}
// Inserts a new element x at index i
insert(i, x) {
this.a.splice(i, 0, x);
this.rebuild();
}
// Removes the element at index i
remove(i) {
this.a.splice(i, 1);
this.rebuild();
}
// Rebuilds the segment tree and polyhash tables
rebuild() {
this.segmentTree = [];
this.polyHashTables = [];
for (let i = 0; i < this.a.length; i += this.segmentSize) {
const polyHashTable = new Map();
let sum = 0;
for (let j = i; j < Math.min(i + this.segmentSize, this.a.length); j++) {
polyHashTable.set(this.a[j], j - i);
sum += this.a[j];
}
this.segmentTree.push(sum);
this.polyHashTables.push(polyHashTable);
}
}
}
// Main method
function main() {
const a = [];
for (let i = 1; i <= 10; i++) {
a.push(i);
}
const dst = new GFG(a);
console.log("Query(0, 4) =", dst.query(0, 4));
dst.update(3, 100);
console.log("Query(2, 5) =", dst.query(2, 5));
}
// Call the main function to execute the program
main();
OutputQuery(0, 4) = 15
Query(2, 5) = 114
Time Complexity: O(N*logN)
Auxiliary Space: O(N)
Conclusion:
In conclusion, the use of Dynamic Segment Trees with Poly Hash Tables is a powerful data structure that provides an efficient and flexible way to maintain dynamic intervals in an array. It is particularly useful in scenarios where the range of values is not known beforehand or changes frequently, making it an important tool in various applications such as online algorithms and data compression.
Similar Reads
Implementation of Hash Table in C/C++ using Separate Chaining
Introduction: Hashing is a technique that maps a large set of data to a small set of data. It uses a hash function for doing this mapping. It is an irreversible process and we cannot find the original value of the key from its hashed value because we are trying to map a large set of data into a smal
10 min read
Implementation of Hashing with Chaining in Python
Hashing is a data structure that is used to store a large amount of data, which can be accessed in O(1) time by operations such as search, insert and delete. Various Applications of Hashing are: Indexing in database Cryptography Symbol Tables in Compiler/Interpreter Dictionaries, caches, etc. Concep
3 min read
Implementing our Own Hash Table with Separate Chaining in Java
All data structure has their own special characteristics, for example, a BST is used when quick searching of an element (in log(n)) is required. A heap or a priority queue is used when the minimum or maximum element needs to be fetched in constant time. Similarly, a hash table is used to fetch, add
10 min read
Implement Simple 2D Segment Tree in Python
A 2D Segment Tree is a data structure that allows efficient querying and updating of two-dimensional arrays, such as matrices. It's an extension of the 1D segment tree, allowing range queries and updates in 2D. Here's a step-by-step implementation of a simple 2D Segment Tree in Python. We'll focus o
5 min read
Segment tree | Efficient implementation
Let us consider the following problem to understand Segment Trees without recursion.We have an array arr[0 . . . n-1]. We should be able to, Find the sum of elements from index l to r where 0 <= l <= r <= n-1Change the value of a specified element of the array to a new value x. We need to d
12 min read
Implementing own Hash Table with Open Addressing Linear Probing
In Open Addressing, all elements are stored in the hash table itself. So at any point, size of table must be greater than or equal to total number of keys (Note that we can increase table size by copying old data if needed). Insert(k) - Keep probing until an empty slot is found. Once an empty slot i
13 min read
B*-Trees implementation in C++
B*-tree of order m is a search tree that is either empty or that satisfies three properties: The root node has minimum two and maximum 2 floor ((2m-2)/3) +1 children Other internal nodes have the minimum floor ((2m-1)/3) and maximum m children All external nodes are on the same level. The advantage
10 min read
Cartesian tree from inorder traversal | Segment Tree
Given an in-order traversal of a cartesian tree, the task is to build the entire tree from it. Examples: Input: arr[] = {1, 5, 3} Output: 1 5 3 5 / \ 1 3 Input: arr[] = {3, 7, 4, 8} Output: 3 7 4 8 8 / 7 / \ 3 4 Approach: We have already seen an algorithm here that takes O(NlogN) time on an average
13 min read
Length of Longest Increasing Subsequences (LIS) using Segment Tree
Given an array arr[] of size N, the task is to count the number of longest increasing subsequences present in the given array. Example: Input: arr[] = {2, 2, 2, 2, 2}Output: 5Explanation: The length of the longest increasing subsequence is 1, i.e. {2}. Therefore, count of longest increasing subseque
15+ min read
Number of subarrays with GCD = 1 | Segment tree
Given an array arr[], the task is to find the count of sub-arrays with GCD equal to 1.Examples: Input: arr[] = {1, 1, 1} Output: 6 Every single subarray of the given array has GCD of 1 and there are a total of 6 subarrays.Input: arr[] = {2, 2, 2} Output: 0 Approach: This problem can be solved in O(N
10 min read