chore: import upstream snapshot with attribution
Awesome CI Workflow / Code Formatter (push) Has been cancelled
Awesome CI Workflow / Compile checks (macOS-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (ubuntu-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (windows-latest) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:37:28 +08:00
commit 29cfe479ab
432 changed files with 68491 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
# If necessary, use the RELATIVE flag, otherwise each source file may be listed
# with full pathname. RELATIVE may makes it easier to extract an executable name
# automatically.
file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp )
# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
foreach( testsourcefile ${APP_SOURCES} )
# I used a simple string replace, to cut off .cpp.
string( REPLACE ".cpp" "" testname ${testsourcefile} )
add_executable( ${testname} ${testsourcefile} )
set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX)
if(OpenMP_CXX_FOUND)
target_link_libraries(${testname} OpenMP::OpenMP_CXX)
endif()
install(TARGETS ${testname} DESTINATION "bin/range_queries")
endforeach( testsourcefile ${APP_SOURCES} )
+135
View File
@@ -0,0 +1,135 @@
/**
* @file
* @brief [Fenwick Tree](https://en.wikipedia.org/wiki/Fenwick_tree) algorithm
* implementation
* @details
* _From Wikipedia, the free encyclopedia._
*
* A Fenwick tree or binary indexed tree (BIT) is a clever implementation of a
* datastructure called bionomal tree. It can update values and solve range
* queries with operations that is; commulative, associative and has an
* inverse for this type of element. It can also solve immutable range queries
* (min/max), when operations only are associative over this type of element.
* Some of these restrictions can be removed, by storing redunant information;
* like in max/min range queries.
*
* @author [Mateusz Grzegorzek](https://github.com/mateusz-grzegorzek)
* @author [David Leal](https://github.com/Panquesito7)
*/
#include <cassert> /// for assert
#include <iostream> /// for IO operations
#include <vector> /// for std::vector
/**
* @namespace
* @brief Range Queries
*/
namespace range_queries {
/**
* @brief The class that initializes the Fenwick Tree.
*/
class fenwick_tree {
size_t n = 0; ///< No. of elements present in input array
std::vector<int> bit{}; ///< Array that represents Binary Indexed Tree.
/**
* @brief Returns the highest power of two which is not more than `x`.
* @param x Index of element in original array.
* @return Offset of index.
*/
inline int offset(int x) { return (x & (-x)); }
public:
/**
* @brief Class Constructor
* @tparam T the type of the array
* @param[in] arr Input array for which prefix sum is evaluated.
* @return void
*/
template <typename T>
explicit fenwick_tree(const std::vector<T>& arr) : n(arr.size()) {
bit.assign(n + 1, 0);
for (int i = 0; i < n; ++i) {
update(i, arr[i]);
}
}
/**
* @brief Class Constructor
* @tparam T the type of the variable
* @param[in] x Size of array that represents Binary Indexed Tree.
* @return void
*/
template <typename T>
explicit fenwick_tree(T x) : n(x) { bit.assign(n + 1, 0); }
/**
* @brief Updates the value of an element in original array and
* accordingly updates the values in BIT array.
* @tparam T the type of the variables
* @param id Index of element in original array.
* @param val Value with which element's value is updated.
* @return void
*/
template <typename T>
void update(T id, T val) {
id++;
while (id <= n) {
bit[id] += val;
id += offset(id);
}
}
/**
* @brief Returns the sum of elements in range from 0 to ID.
* @tparam T the type of the variables
* @param id Index in original array up to which sum is evaluated.
* @return Sum of elements in range from 0 to id.
*/
template <typename T>
int sum(T id) {
id++;
T res = 0;
while (id > 0) {
res += bit[id];
id -= offset(id);
}
return res;
}
/**
* @brief Returns the prefix sum in range from L to R.
* @param l Left index of range.
* @param r Right index of range.
* @return Sum of elements in range from L to R.
*/
int sum_range(int l, int r) { return sum(r) - sum(l - 1); }
};
} // namespace range_queries
/**
* @brief Self-test implementations
* @returns void
*/
static void tests() {
std::vector<int> arr = {1, 2, 3, 4, 5};
range_queries::fenwick_tree fenwick_tree(arr);
assert(fenwick_tree.sum_range(0, 0) == 1);
assert(fenwick_tree.sum_range(0, 1) == 3);
assert(fenwick_tree.sum_range(0, 2) == 6);
assert(fenwick_tree.sum_range(0, 3) == 10);
assert(fenwick_tree.sum_range(0, 4) == 15);
fenwick_tree.update(0, 6);
std::cout << "All tests have successfully passed!\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
tests(); // run self-test implementations
return 0;
}
+646
View File
@@ -0,0 +1,646 @@
/**
* @file
* @brief [Heavy Light
* Decomposition](https://en.wikipedia.org/wiki/Heavy_path_decomposition)
* implementation
* @author [Aniruthan R](https://github.com/aneee004)
*
* @details
* Heavy-Light Decomposition is a technique on trees, that supports the
* following:
* 1. Update node s, with a value v
* 2. Return the (sum) of all node values on the simple path from a to b
* (sum) can also be replced with XOR, OR, AND, min, or max
*
* The update is done in O(log n) time, and
* the query is done in O(log^2 n) time with HLD
* where, n is the number of nodes
*
* The template type is the data type of the value stored in the nodes.
* If a non-primitive data-type is used as a template,
* the coressponding operators must be overloaded.
*
* An HLD object can only be created with a constant number of nodes, and
* it cannot be changed later. Creaty an empty instance is not supported.
*
* To start answering updates and queries,
* 1. Create an instance of HLD<X> object (obj), with the required data type.
* 2. Read in the edge/parent information and update it with obj.add_edge().
* Note: The edges addes must be 0 indexed.
* 3. Create a vector with initial node values, and call set_node_val() with it.
* 4. Call obj.init() to populate the required information for supporting
* operations.
* 5. Call obj.update(node, new_val), to update the value at index 'node' to the
* new value. Note: node must be 0 indexed
* 6. Call obj.query(a, b) to get the (sum) of node values in the simple path
* from a to b. Note: a and b, must be 0 indexed.
*
* Sample I/O at the bottom.
* @todo Support edge weight queries, by storing the edge weight value in it's
* child algorithm verified by testing in CSES path queries:
* https://cses.fi/problemset/task/1138
*/
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstring>
#include <iostream>
#include <list>
#include <numeric>
#include <string>
#include <vector>
/**
* @namespace range_queries
* @brief Algorithms and Data Structures that support range queries and updates.
*/
namespace range_queries {
/**
* @namespace heavy_light_decomposition
* @brief Heavy light decomposition algorithm
*/
namespace heavy_light_decomposition {
/**
* @brief A Basic Tree, which supports binary lifting
* @tparam the data type of the values stored in the tree nodes
* @details Deleting the default constructor
* An instance can only be created with the number of nodes
* Defaults:
* t_node indexing are zero based
* t_root is 0
* depth of root_node is 0
* Supports:
* lift :- lift a node k units up the tree
* kth_ancestor :- returns the kth ancestor
* lca :- returns the least common ancestor
*/
template <typename X>
class Tree {
//
private:
std::vector<std::list<int>>
t_adj; ///< an adjacency list to stores the tree edges
const int t_nodes, ///< number of nodes
t_maxlift; ///< maximum possible height of the tree
std::vector<std::vector<int>>
t_par; ///< a matrix to store every node's 2^kth parent
std::vector<int> t_depth, ///< a vector to store the depth of a node,
t_size; ///< a vector to store the subtree size rooted at node
int t_root; ///< the root of the tree
std::vector<X> t_val; ///< values of nodes
template <typename T>
friend class HLD;
/**
* @brief Utility function to compute sub-tree sizes
* @param u current dfs node
* @param p the parent of node @param u
* @returns void
*/
void dfs_size(int u, int p = -1) {
for (const int &v : t_adj[u]) {
if (v ^ p) {
dfs_size(v, u);
t_size[u] += t_size[v];
}
}
}
/**
* @brief Utility function to populate the t_par vector
* @param u current dfs node
* @param p the parent of node u
* @returns void
*/
void dfs_lca(int u, int p = -1) {
t_par[u][0] = p;
if (p != -1) {
t_depth[u] = 1 + t_depth[p];
}
for (int k = 1; k < t_maxlift; k++) {
if (t_par[u][k - 1] != -1) {
t_par[u][k] = t_par[t_par[u][k - 1]][k - 1];
}
}
for (const int &v : t_adj[u]) {
if (v ^ p) {
dfs_lca(v, u);
}
}
}
public:
/**
* @brief Class parameterized constructor, resizes the and initializes the
* data members
* @param nodes the total number of nodes in the tree
*/
explicit Tree(int nodes)
: t_nodes(nodes), t_maxlift(static_cast<int>(floor(log2(nodes))) + 1) {
/* Initialize and resize all the vectors */
t_root = 0; /* Default */
t_adj.resize(t_nodes);
t_par.assign(t_nodes, std::vector<int>(t_maxlift, -1));
t_depth.assign(t_nodes, 0);
t_size.assign(t_nodes, 1);
t_val.resize(t_nodes);
}
/**
* @brief Adds an undirected edge from node u to node v in the tree
* @param u the node where the edge is from
* @param v the node where the edge is to
* @returns void
*/
void add_edge(const int u, const int v) {
t_adj[u].push_back(v);
t_adj[v].push_back(u);
}
/**
* @brief Set the root for the tree
* @param new_root the new root
* @returns void
*/
void change_root(int new_root) { t_root = new_root; }
/**
* @brief Set the values for all the nodes
* @param node_val a vector of size n, with all the node values where, n is
* the number of nodes
* @returns void
*/
void set_node_val(const std::vector<X> &node_val) {
assert(static_cast<int>(node_val.size()) == t_nodes);
t_val = node_val;
}
/**
* @brief This function must be called after the tree adjacency list and
* node values are populated The function initializes the required
* parameters, and populates the segment tree
* @returns void
*/
void init() {
assert(t_nodes > 0);
dfs_size(t_root);
dfs_lca(t_root);
}
/**
* @brief The function lifts a node, k units up the tree.
* The lifting is done in place, and the result is stored in the address
* pointed by p.
* @param p a pointer to the variable that stores the node id
* @param dist the distance to move up the tree
* @returns void
*/
void lift(int *const p, int dist) {
for (int k = 0; k < t_maxlift; k++) {
if (*p == -1) {
return;
}
if (dist & 1) {
*p = t_par[*p][k];
}
dist >>= 1;
}
}
/**
* @brief The function returns the kth ancestor of a node
* @param p the node id whose kth ancestor is to be found
* @param dist the distance to move up the tree
* @returns the kth ancestor of node
*/
int kth_ancestor(int p, const int &dist) {
lift(&p, dist);
return p;
}
/**
* @brief The function returns the least common ancestor of two nodes
* @param a node id_1
* @param b node id_2
* @returns the least common ancestor of node a, and node b
*/
int lca(int a, int b) {
assert(a >= 0 and b >= 0 and a < t_nodes and b < t_nodes);
if (t_depth[a] > t_depth[b]) {
lift(&a, t_depth[a] - t_depth[b]);
}
if (t_depth[b] > t_depth[a]) {
lift(&b, t_depth[b] - t_depth[a]);
}
if (a == b) {
return a;
}
for (int k = t_maxlift - 1; k >= 0; k--) {
if (t_par[a][k] != t_par[b][k]) {
a = t_par[a][k];
b = t_par[b][k];
}
}
return t_par[a][0];
}
};
/**
* @brief Segment Tree, to store heavy chains
* @tparam the data type of the values stored in the tree nodes
*/
template <typename X>
class SG {
private:
/**
* @brief Everything here is private,
* and can only be accessed through the methods,
* in the derived class (HLD)
*/
std::vector<X> s_tree; ///< the segment tree, stored as a vector
int s_size; ///< number of leaves in the segment tree
X sret_init = 0; ///< inital query return value
template <typename T>
friend class HLD;
/**
* @brief Function that specifies the type of operation involved when
* segments are combined
* @param lhs the left segment
* @param rhs the right segment
* @returns the combined result
*/
X combine(X lhs, X rhs) { return lhs + rhs; }
/**
* @brief Class parameterized constructor. Resizes the and initilizes the
* data members.
* @param nodes the total number of nodes in the tree
* @returns void
*/
explicit SG(int size) {
s_size = size;
s_tree.assign(2 * s_size, 0ll);
}
/**
* @brief Update the value at a node
* @param p the node to be udpated
* @param v the update value
* @returns void
*/
void update(int p, X v) {
for (p += s_size; p > 0; p >>= 1) {
s_tree[p] += v;
}
}
/**
* @brief Make a range query from node label l to node label r
* @param l node label where the path starts
* @param r node label where the path ends
* @returns void
*/
X query(int l, int r) {
X lhs = sret_init, rhs = sret_init;
for (l += s_size, r += s_size + 1; l < r; l >>= 1, r >>= 1) {
if (l & 1) {
lhs = combine(lhs, s_tree[l++]);
}
if (r & 1) {
rhs = combine(s_tree[--r], rhs);
}
}
return combine(lhs, rhs);
}
/**
* @brief Set the initialization for the query data type, based on
* requirement
*
* @details
* Change the sret_init, based on requirement:
* * Sum Query: 0 (Default)
* * XOR Query: 0 (Default)
* * Min Query: Infinity
* * Max Query: -Infinity
* @param new_sret_init the new init
*/
void set_sret_init(X new_sret_init) { sret_init = new_sret_init; }
};
/**
* @brief The Heavy-Light Decomposition class
* @tparam the data type of the values stored in the tree nodes
*/
template <typename X>
class HLD : public Tree<X>, public SG<X> {
private:
int label; ///< utility member to assign labels in dfs_labels()
std::vector<int> h_label, ///< stores the label of a node
h_heavychlid, ///< stores the heavy child of a node
h_parent; ///< stores the top of the heavy chain from a node
/**
* @brief Utility function to assign heavy child to each node (-1 for a leaf
* node)
* @param u current dfs node
* @param p the parent of node u
* @returns void
*/
void dfs_hc(int u, int p = -1) {
int hc_size = -1, hc_id = -1;
for (const int &v : Tree<X>::t_adj[u]) {
if (v ^ p) {
dfs_hc(v, u);
if (Tree<X>::t_size[v] > hc_size) {
hc_size = Tree<X>::t_size[v];
hc_id = v;
}
}
}
h_heavychlid[u] = hc_id;
}
/**
* @brief Utility function to assign highest parent that can be reached
* though heavy chains
* @param u current dfs node
* @param p the parent of node u
* @returns void
*/
void dfs_par(int u, int p = -1) {
if (h_heavychlid[u] != -1) {
h_parent[h_heavychlid[u]] = h_parent[u];
dfs_par(h_heavychlid[u], u);
}
for (const int &v : Tree<X>::t_adj[u]) {
if (v ^ p and v ^ h_heavychlid[u]) {
dfs_par(v, u);
}
}
}
/**
* @brief Utility function to lable the nodes so that heavy chains have a
* contigous lable
* @param u current dfs node
* @param p the parent of node u
* @returns void
*/
void dfs_labels(int u, int p = -1) {
h_label[u] = label++;
if (h_heavychlid[u] != -1) {
dfs_labels(h_heavychlid[u], u);
}
for (const int &v : Tree<X>::t_adj[u]) {
if (v ^ p and v ^ h_heavychlid[u]) {
dfs_labels(v, u);
}
}
}
/**
* @brief Utility function to break down a path query into two chain queries
* @param a node where the path starts
* @param b node where the path ends
* a and b must belong to a single root to leaf chain
* @returns the sum of ndoe values in the simple path from a to b
*/
X chain_query(int a, int b) {
X ret = SG<X>::sret_init;
if (Tree<X>::t_depth[a] < Tree<X>::t_depth[b]) {
std::swap(a, b);
}
while (Tree<X>::t_depth[a] >= Tree<X>::t_depth[b]) {
int l = h_label[h_parent[a]];
int r = h_label[a];
if (Tree<X>::t_depth[h_parent[a]] < Tree<X>::t_depth[b]) {
l += Tree<X>::t_depth[b] - Tree<X>::t_depth[h_parent[a]];
}
ret = SG<X>::combine(ret, SG<X>::query(l, r));
a = Tree<X>::t_par[h_parent[a]][0];
if (a == -1) {
break;
}
}
return ret;
}
public:
/**
* @brief Class parameterized constructor. Resizes the and initilizes the
* data members.
* @param nodes the total number of nodes in the tree
*/
explicit HLD(int nodes) : Tree<X>(nodes), SG<X>(nodes) {
/* Initialization and resize vectors */
label = 0;
h_label.assign(Tree<X>::t_nodes, -1);
h_heavychlid.assign(Tree<X>::t_nodes, -1);
h_parent.resize(Tree<X>::t_nodes);
iota(h_parent.begin(), h_parent.end(), 0);
}
/**
* @brief This function must be called after the tree adjacency list and
* node values are populated The function initializes the required
* parametes, and populates the segment tree
* @returns void
*/
void init() {
Tree<X>::init();
// Fill the heavy child, greatest parent, and labels
label = 0;
dfs_hc(Tree<X>::t_root);
dfs_par(Tree<X>::t_root);
dfs_labels(Tree<X>::t_root);
// Segment Tree Initialization
for (int i = 0; i < Tree<X>::t_nodes; i++) {
SG<X>::s_tree[h_label[i] + Tree<X>::t_nodes] = Tree<X>::t_val[i];
}
for (int i = Tree<X>::t_nodes - 1; i > 0; i--) {
SG<X>::s_tree[i] = SG<X>::combine(SG<X>::s_tree[i << 1],
SG<X>::s_tree[i << 1 | 1]);
}
}
/**
* @brief This function updates the value at node with val
* @param node the node where the update is done
* @param val the value that is being updated
* @returns void
*/
void update(int node, X val) {
X diff = val - Tree<X>::t_val[node];
SG<X>::update(h_label[node], diff);
Tree<X>::t_val[node] = val;
}
/**
* @brief This function returns the sum of node values in the simple path
* from from node_1 to node_2
* @param a the node where the simple path starts
* @param b the node where the simple path ends
* (parameters are interchangeable, i.e., the function is commutative)
* @returns the sum of node values in the simple path from a to b
*/
X query(int a, int b) {
int lc = Tree<X>::lca(a, b);
X ret = SG<X>::sret_init;
assert(lc != -1);
ret += chain_query(a, lc);
ret += chain_query(b, lc);
return ret - Tree<X>::t_val[lc];
}
};
} // namespace heavy_light_decomposition
} // namespace range_queries
/**
* Test implementations
* @returns none
*/
static void test_1() {
std::cout << "Test 1:\n";
// Test details
int n = 5;
std::vector<int64_t> node_values = {4, 2, 5, 2, 1};
std::vector<std::vector<int>> edges = {{1, 2}, {1, 3}, {3, 4}, {3, 5}};
std::vector<std::vector<int>> queries = {
{2, 1, 4},
{1, 3, 2},
{2, 1, 4},
};
std::vector<int> expected_result = {11, 8};
std::vector<int> code_result;
range_queries::heavy_light_decomposition::HLD<int64_t> hld(n);
hld.set_node_val(node_values);
for (int i = 0; i < n - 1; i++) {
int u = edges[i][0], v = edges[i][1];
hld.add_edge(u - 1, v - 1);
}
hld.init();
for (const auto &q : queries) {
int type = q[0];
if (type == 1) {
int p = q[1], x = q[2];
hld.update(p - 1, x);
} else if (type == 2) {
int a = q[1], b = q[2];
code_result.push_back(hld.query(a - 1, b - 1));
} else {
continue;
}
}
for (int i = 0; i < static_cast<int>(expected_result.size()); i++) {
assert(expected_result[i] == code_result[i]);
}
std::cout << "\nTest 1 passed!\n";
}
/**
* Second test implementations
* @returns void
*/
static void test_2() {
std::cout << "Test 2:\n";
// Test details (Bamboo)
int n = 10;
std::vector<int64_t> node_values = {1, 8, 6, 8, 6, 2, 9, 2, 3, 2};
std::vector<std::vector<int>> edges = {{10, 5}, {6, 2}, {10, 7},
{5, 2}, {3, 9}, {8, 3},
{1, 4}, {6, 4}, {8, 7}};
std::vector<std::vector<int>> queries = {
{2, 1, 10}, {2, 1, 6}, {1, 3, 4}, {2, 1, 9}, {1, 5, 3},
{1, 7, 8}, {2, 1, 4}, {2, 1, 8}, {1, 1, 4}, {1, 2, 7}};
std::vector<int> expected_result = {27, 11, 45, 9, 34};
std::vector<int> code_result;
range_queries::heavy_light_decomposition::HLD<int64_t> hld(n);
hld.set_node_val(node_values);
for (int i = 0; i < n - 1; i++) {
int u = edges[i][0], v = edges[i][1];
hld.add_edge(u - 1, v - 1);
}
hld.init();
for (const auto &q : queries) {
int type = q[0];
if (type == 1) {
int p = q[1], x = q[2];
hld.update(p - 1, x);
} else if (type == 2) {
int a = q[1], b = q[2];
code_result.push_back(hld.query(a - 1, b - 1));
} else {
continue;
}
}
for (int i = 0; i < static_cast<int>(expected_result.size()); i++) {
assert(expected_result[i] == code_result[i]);
}
std::cout << "\nTest2 passed!\n";
}
/**
* Third test implementations
* @returns void
*/
static void test_3() {
std::cout << "Test 3:\n";
// Test details
int n = 8;
std::vector<int64_t> node_values = {1, 8, 6, 8, 6, 2, 9, 2};
std::vector<std::vector<int>> edges = {{1, 2}, {2, 3}, {3, 4}, {1, 5},
{6, 3}, {7, 5}, {8, 7}};
std::vector<std::vector<int>> queries = {
{2, 6, 8}, {2, 3, 6}, {1, 3, 4}, {2, 7, 1}, {1, 5, 3},
{1, 7, 8}, {2, 6, 4}, {2, 7, 8}, {1, 1, 4}, {1, 2, 7}};
std::vector<int> expected_result = {34, 8, 16, 14, 10};
std::vector<int> code_result;
range_queries::heavy_light_decomposition::HLD<int64_t> hld(n);
hld.set_node_val(node_values);
for (int i = 0; i < n - 1; i++) {
int u = edges[i][0], v = edges[i][1];
hld.add_edge(u - 1, v - 1);
}
hld.init();
for (const auto &q : queries) {
int type = q[0];
if (type == 1) {
int p = q[1], x = q[2];
hld.update(p - 1, x);
} else if (type == 2) {
int a = q[1], b = q[2];
code_result.push_back(hld.query(a - 1, b - 1));
} else {
continue;
}
}
for (int i = 0; i < static_cast<int>(expected_result.size()); i++) {
assert(expected_result[i] == code_result[i]);
}
std::cout << "\nTest3 passed!\n";
}
/**
* Main function
*/
int main() {
test_1();
test_2();
test_3();
return 0;
}
+67
View File
@@ -0,0 +1,67 @@
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;
const int N = 1e6 + 5;
int a[N], bucket[N], cnt[N];
int bucket_size;
struct query {
int l, r, i;
} q[N];
int ans = 0;
void add(int index) {
cnt[a[index]]++;
if (cnt[a[index]] == 1)
ans++;
}
void remove(int index) {
cnt[a[index]]--;
if (cnt[a[index]] == 0)
ans--;
}
bool mycmp(query x, query y) {
if (x.l / bucket_size != y.l / bucket_size)
return x.l / bucket_size < y.l / bucket_size;
return x.r < y.r;
}
int main() {
int n, t, i;
scanf("%d", &n);
for (i = 0; i < n; i++) scanf("%d", &a[i]);
bucket_size = ceil(sqrt(n));
scanf("%d", &t);
for (i = 0; i < t; i++) {
scanf("%d %d", &q[i].l, &q[i].r);
q[i].l--;
q[i].r--;
q[i].i = i;
}
sort(q, q + t, mycmp);
int left = 0, right = 0;
for (i = 0; i < t; i++) {
int L = q[i].l, R = q[i].r;
while (left < L) {
remove(left);
left++;
}
while (left > L) {
add(left - 1);
left--;
}
while (right <= R) {
add(right);
right++;
}
while (right > R + 1) {
remove(right - 1);
right--;
}
bucket[q[i].i] = ans;
}
for (i = 0; i < t; i++) printf("%d\n", bucket[i]);
return 0;
}
@@ -0,0 +1,322 @@
/**
* @file
* @brief [Persistent segment tree with range updates (lazy
* propagation)](https://en.wikipedia.org/wiki/Persistent_data_structure)
*
* @details
* A normal segment tree facilitates making point updates and range queries in
* logarithmic time. Lazy propagation preserves the logarithmic time with range
* updates. So, a segment tree with lazy propagation enables doing range updates
* and range queries in logarithmic time, but it doesn't save any information
* about itself before the last update. A persistent data structure always
* preserves the previous version of itself when it is modified. That is, a new
* version of the segment tree is generated after every update. It saves all
* previous versions of itself (before every update) to facilitate doing range
* queries in any version. More memory is used ,but the logarithmic time is
* preserved because the new version points to the same nodes, that the previous
* version points to, that are not affected by the update. That is, only the
* nodes that are affected by the update and their ancestors are copied. The
* rest is copied using lazy propagation in the next queries. Thus preserving
* the logarithmic time because the number of nodes copied after any update is
* logarithmic.
*
* @author [Magdy Sedra](https://github.com/MSedra)
*/
#include <cstdint> /// for std::uint32_t
#include <iostream> /// for IO operations
#include <memory> /// to manage dynamic memory
#include <vector> /// for std::vector
/**
* @namespace range_queries
* @brief Range queries algorithms
*/
namespace range_queries {
/**
* @brief Range query here is range sum, but the code can be modified to make
* different queries like range max or min.
*/
class perSegTree {
private:
class Node {
public:
std::shared_ptr<Node> left = nullptr; /// pointer to the left node
std::shared_ptr<Node> right = nullptr; /// pointer to the right node
int64_t val = 0,
prop = 0; /// val is the value of the node (here equals to the
/// sum of the leaf nodes children of that node),
/// prop is the value to be propagated/added to all
/// the leaf nodes children of that node
};
uint32_t n = 0; /// number of elements/leaf nodes in the segment tree
std::vector<std::shared_ptr<Node>>
ptrs{}; /// ptrs[i] holds a root pointer to the segment tree after the
/// ith update. ptrs[0] holds a root pointer to the segment
/// tree before any updates
std::vector<int64_t> vec{}; /// values of the leaf nodes that the segment
/// tree will be constructed with
/**
* @brief Creating a new node with the same values of curr node
* @param curr node that would be copied
* @returns the new node
*/
std::shared_ptr<Node> newKid(std::shared_ptr<Node> const &curr) {
auto newNode = std::make_shared<Node>(Node());
newNode->left = curr->left;
newNode->right = curr->right;
newNode->prop = curr->prop;
newNode->val = curr->val;
return newNode;
}
/**
* @brief If there is some value to be propagated to the passed node, value
* is added to the node and the children of the node, if exist, are copied
* and the propagated value is also added to them
* @param i the left index of the range that the passed node holds its sum
* @param j the right index of the range that the passed node holds its sum
* @param curr pointer to the node to be propagated
* @returns void
*/
void lazy(const uint32_t &i, const uint32_t &j,
std::shared_ptr<Node> const &curr) {
if (!curr->prop) {
return;
}
curr->val += (j - i + 1) * curr->prop;
if (i != j) {
curr->left = newKid(curr->left);
curr->right = newKid(curr->right);
curr->left->prop += curr->prop;
curr->right->prop += curr->prop;
}
curr->prop = 0;
}
/**
* @brief Constructing the segment tree with the early passed vector. Every
* call creates a node to hold the sum of the given range, set its pointers
* to the children, and set its value to the sum of the children's values
* @param i the left index of the range that the created node holds its sum
* @param j the right index of the range that the created node holds its sum
* @returns pointer to the newly created node
*/
std::shared_ptr<Node> construct(const uint32_t &i, const uint32_t &j) {
auto newNode = std::make_shared<Node>(Node());
if (i == j) {
newNode->val = vec[i];
} else {
uint32_t mid = i + (j - i) / 2;
auto leftt = construct(i, mid);
auto right = construct(mid + 1, j);
newNode->val = leftt->val + right->val;
newNode->left = leftt;
newNode->right = right;
}
return newNode;
}
/**
* @brief Doing range update, checking at every node if it has some value to
* be propagated. All nodes affected by the update are copied and
* propagation value is added to the leaf of them
* @param i the left index of the range that the passed node holds its sum
* @param j the right index of the range that the passed node holds its sum
* @param l the left index of the range to be updated
* @param r the right index of the range to be updated
* @param value the value to be added to every element whose index x
* satisfies l<=x<=r
* @param curr pointer to the current node, which has value = the sum of
* elements whose index x satisfies i<=x<=j
* @returns pointer to the current newly created node
*/
std::shared_ptr<Node> update(const uint32_t &i, const uint32_t &j,
const uint32_t &l, const uint32_t &r,
const int64_t &value,
std::shared_ptr<Node> const &curr) {
lazy(i, j, curr);
if (i >= l && j <= r) {
std::shared_ptr<Node> newNode = newKid(curr);
newNode->prop += value;
lazy(i, j, newNode);
return newNode;
}
if (i > r || j < l) {
return curr;
}
auto newNode = std::make_shared<Node>(Node());
uint32_t mid = i + (j - i) / 2;
newNode->left = update(i, mid, l, r, value, curr->left);
newNode->right = update(mid + 1, j, l, r, value, curr->right);
newNode->val = newNode->left->val + newNode->right->val;
return newNode;
}
/**
* @brief Querying the range from index l to index r, checking at every node
* if it has some value to be propagated. Current node's value is returned
* if its range is completely inside the wanted range, else 0 is returned
* @param i the left index of the range that the passed node holds its sum
* @param j the right index of the range that the passed node holds its sum
* @param l the left index of the range whose sum should be returned as a
* result
* @param r the right index of the range whose sum should be returned as a
* result
* @param curr pointer to the current node, which has value = the sum of
* elements whose index x satisfies i<=x<=j
* @returns sum of elements whose index x satisfies l<=x<=r
*/
int64_t query(const uint32_t &i, const uint32_t &j, const uint32_t &l,
const uint32_t &r, std::shared_ptr<Node> const &curr) {
lazy(i, j, curr);
if (j < l || r < i) {
return 0;
}
if (i >= l && j <= r) {
return curr->val;
}
uint32_t mid = i + (j - i) / 2;
return query(i, mid, l, r, curr->left) +
query(mid + 1, j, l, r, curr->right);
}
/**
* public methods that can be used directly from outside the class. They
* call the private functions that do all the work
*/
public:
/**
* @brief Constructing the segment tree with the values in the passed
* vector. Returned root pointer is pushed in the pointers vector to have
* access to the original version if the segment tree is updated
* @param vec vector whose values will be used to build the segment tree
* @returns void
*/
void construct(const std::vector<int64_t>
&vec) // the segment tree will be built from the values
// in "vec", "vec" is 0 indexed
{
if (vec.empty()) {
return;
}
n = vec.size();
this->vec = vec;
auto root = construct(0, n - 1);
ptrs.push_back(root);
}
/**
* @brief Doing range update by passing the left and right indexes of the
* range as well as the value to be added.
* @param l the left index of the range to be updated
* @param r the right index of the range to be updated
* @param value the value to be added to every element whose index x
* satisfies l<=x<=r
* @returns void
*/
void update(const uint32_t &l, const uint32_t &r,
const int64_t
&value) // all elements from index "l" to index "r" would
// by updated by "value", "l" and "r" are 0 indexed
{
ptrs.push_back(update(
0, n - 1, l, r, value,
ptrs[ptrs.size() -
1])); // saving the root pointer to the new segment tree
}
/**
* @brief Querying the range from index l to index r, getting the sum of the
* elements whose index x satisfies l<=x<=r
* @param l the left index of the range whose sum should be returned as a
* result
* @param r the right index of the range whose sum should be returned as a
* result
* @param version the version to query on. If equals to 0, the original
* segment tree will be queried
* @returns sum of elements whose index x satisfies l<=x<=r
*/
int64_t query(
const uint32_t &l, const uint32_t &r,
const uint32_t
&version) // querying the range from "l" to "r" in a segment tree
// after "version" updates, "l" and "r" are 0 indexed
{
return query(0, n - 1, l, r, ptrs[version]);
}
/**
* @brief Getting the number of versions after updates so far which is equal
* to the size of the pointers vector
* @returns the number of versions
*/
uint32_t size() // returns the number of segment trees (versions) , the
// number of updates done so far = returned value - 1
// ,because one of the trees is the original segment tree
{
return ptrs.size();
}
};
} // namespace range_queries
/**
* @brief Test implementations
* @returns void
*/
static void test() {
std::vector<int64_t> arr = {-5, 2, 3, 11, -2, 7, 0, 1};
range_queries::perSegTree tree;
std::cout << "Elements before any updates are {";
for (uint32_t i = 0; i < arr.size(); ++i) {
std::cout << arr[i];
if (i != arr.size() - 1) {
std::cout << ",";
}
}
std::cout << "}\n";
tree.construct(
arr); // constructing the original segment tree (version = 0)
std::cout << "Querying range sum on version 0 from index 2 to 4 = 3+11-2 = "
<< tree.query(2, 4, 0) << '\n';
std::cout
<< "Subtract 7 from all elements from index 1 to index 5 inclusive\n";
tree.update(1, 5, -7); // subtracting 7 from index 1 to index 5
std::cout << "Elements of the segment tree whose version = 1 (after 1 "
"update) are {";
for (uint32_t i = 0; i < arr.size(); ++i) {
std::cout << tree.query(i, i, 1);
if (i != arr.size() - 1) {
std::cout << ",";
}
}
std::cout << "}\n";
std::cout << "Add 10 to all elements from index 0 to index 7 inclusive\n";
tree.update(0, 7, 10); // adding 10 to all elements
std::cout << "Elements of the segment tree whose version = 2 (after 2 "
"updates) are {";
for (uint32_t i = 0; i < arr.size(); ++i) {
std::cout << tree.query(i, i, 2);
if (i != arr.size() - 1) {
std::cout << ",";
}
}
std::cout << "}\n";
std::cout << "Number of segment trees (versions) now = " << tree.size()
<< '\n';
std::cout << "Querying range sum on version 0 from index 3 to 5 = 11-2+7 = "
<< tree.query(3, 5, 0) << '\n';
std::cout << "Querying range sum on version 1 from index 3 to 5 = 4-9+0 = "
<< tree.query(3, 5, 1) << '\n';
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}
+85
View File
@@ -0,0 +1,85 @@
/**
* @file
* @brief
* [Prefix Sum
* Array](https://en.wikipedia.org/wiki/Prefix_sum) data structure
* implementation.
*
* @details
* Prefix Sum Array is a data structure, that allows answering sum in some range
* queries. It can answer most sum range queries in O(1), with a build time
* complexity of O(N). But it hasn't an update querie.
*
* * Running Time Complexity \n
* * Build : O(N) \n
* * Range Query : O(1) \n
* @author [Paulo Vitor Lima Borges](https://github.com/PauloVLB)
*/
#include <cassert> /// for assert
#include <iostream> /// for IO operations
#include <vector> /// for std::vector
/**
* @namespace range_queries
* @brief Range Queries algorithms
*/
namespace range_queries {
/**
* @namespace prefix_sum_array
* @brief Range sum queries using prefix-sum-array
*/
namespace prefix_sum_array {
std::vector<int64_t> PSA{};
/**
* @brief function that builds the PSA
* @param original_array original array of values
* @returns void
*/
void build(const std::vector<int64_t>& original_array) {
PSA.clear();
PSA.push_back(0);
for (std::size_t i = 1; i < original_array.size(); ++i) {
PSA.push_back(PSA.back() + original_array[i]);
}
}
/**
* @brief query function
* @param beg begin of the interval to sum
* @param end end of the interval to sum
* @returns sum of the range [beg, end]
*/
int64_t query(int64_t beg, int64_t end) { return PSA[end] - PSA[beg - 1]; }
} // namespace prefix_sum_array
} // namespace range_queries
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
std::vector<int64_t> values{0, 123, 0, 2, -2, 5,
24, 0, 23, -1, -1}; // original array
range_queries::prefix_sum_array::build(values);
// queries are of the type: sum of the range [a, b] = psa[b] - psa[a-1]
assert(range_queries::prefix_sum_array::query(1, 10) ==
173); // sum of the entire array
assert(range_queries::prefix_sum_array::query(4, 6) ==
27); // the sum of the interval [4, 6]
assert(range_queries::prefix_sum_array::query(5, 9) ==
51); // the sum of the interval [5, 9]
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run self-test implementations
return 0;
}
+219
View File
@@ -0,0 +1,219 @@
/**
* @file
* @brief Implementation of [Segment Tree]
* (https://en.wikipedia.org/wiki/Segment_tree) data structure
*
* @details
* A segment tree, also known as a statistic tree, is a tree data structure used
* for storing information about intervals, or segments. Its classical version
* allows querying which of the stored segments contain a given point, but our
* modification allows us to perform (query) any binary operation on any range
* in the array in O(logN) time. Here, we have used addition (+).
* For range updates, we have used lazy propagation.
*
* * Space Complexity : O(NlogN) \n
* * Build Time Complexity : O(NlogN) \n
* * Query Time Complexity : O(logN) \n
*
* @author [Madhav Gaba](https://github.com/madhavgaba)
* @author [Soham Roy](https://github.com/sohamroy19)
*/
#include <cassert> /// for assert
#include <cmath> /// for log2
#include <cstdint> /// for std::uint64_t
#include <iostream> /// for IO operations
#include <vector> /// for std::vector
/**
* @brief Constructs the initial segment tree
*
* @param arr input to construct the tree out of
* @param segtree the segment tree
* @param low inclusive lowest index of arr to begin at
* @param high inclusive highest index of arr to end at
* @param pos index of segtree to fill (eg. root node)
* @returns void
*/
void ConsTree(const std::vector<int64_t> &arr, std::vector<int64_t> *segtree,
uint64_t low, uint64_t high, uint64_t pos) {
if (low == high) {
(*segtree)[pos] = arr[low];
return;
}
uint64_t mid = (low + high) / 2;
ConsTree(arr, segtree, low, mid, 2 * pos + 1);
ConsTree(arr, segtree, mid + 1, high, 2 * pos + 2);
(*segtree)[pos] = (*segtree)[2 * pos + 1] + (*segtree)[2 * pos + 2];
}
/**
* @brief Returns the sum of all elements in a range
*
* @param segtree the segment tree
* @param lazy for lazy propagation
* @param qlow lower index of the required query
* @param qhigh higher index of the required query
* @param low lower index of query for this function call
* @param high higher index of query for this function call
* @param pos index of segtree to consider (eg. root node)
* @return result of the range query for this function call
*/
int64_t query(std::vector<int64_t> *segtree, std::vector<int64_t> *lazy,
uint64_t qlow, uint64_t qhigh, uint64_t low, uint64_t high,
uint64_t pos) {
if (low > high || qlow > high || low > qhigh) {
return 0;
}
if ((*lazy)[pos] != 0) {
(*segtree)[pos] += (*lazy)[pos] * (high - low + 1);
if (low != high) {
(*lazy)[2 * pos + 1] += (*lazy)[pos];
(*lazy)[2 * pos + 2] += (*lazy)[pos];
}
(*lazy)[pos] = 0;
}
if (qlow <= low && qhigh >= high) {
return (*segtree)[pos];
}
uint64_t mid = (low + high) / 2;
return query(segtree, lazy, qlow, qhigh, low, mid, 2 * pos + 1) +
query(segtree, lazy, qlow, qhigh, mid + 1, high, 2 * pos + 2);
}
/**
* @brief Updates a range of the segment tree
*
* @param segtree the segment tree
* @param lazy for lazy propagation
* @param start lower index of the required query
* @param end higher index of the required query
* @param delta integer to add to each element of the range
* @param low lower index of query for this function call
* @param high higher index of query for this function call
* @param pos index of segtree to consider (eg. root node)
* @returns void
*/
void update(std::vector<int64_t> *segtree, std::vector<int64_t> *lazy,
int64_t start, int64_t end, int64_t delta, uint64_t low,
uint64_t high, uint64_t pos) {
if (low > high) {
return;
}
if ((*lazy)[pos] != 0) {
(*segtree)[pos] += (*lazy)[pos] * (high - low + 1);
if (low != high) {
(*lazy)[2 * pos + 1] += (*lazy)[pos];
(*lazy)[2 * pos + 2] += (*lazy)[pos];
}
(*lazy)[pos] = 0;
}
if (start > high || end < low) {
return;
}
if (start <= low && end >= high) {
(*segtree)[pos] += delta * (high - low + 1);
if (low != high) {
(*lazy)[2 * pos + 1] += delta;
(*lazy)[2 * pos + 2] += delta;
}
return;
}
uint64_t mid = (low + high) / 2;
update(segtree, lazy, start, end, delta, low, mid, 2 * pos + 1);
update(segtree, lazy, start, end, delta, mid + 1, high, 2 * pos + 2);
(*segtree)[pos] = (*segtree)[2 * pos + 1] + (*segtree)[2 * pos + 2];
}
/**
* @brief Self-test implementation
*
* @returns void
*/
static void test() {
auto max = static_cast<int64_t>(2 * pow(2, ceil(log2(7))) - 1);
assert(max == 15);
std::vector<int64_t> arr{1, 2, 3, 4, 5, 6, 7}, lazy(max), segtree(max);
ConsTree(arr, &segtree, 0, 7 - 1, 0);
assert(query(&segtree, &lazy, 1, 5, 0, 7 - 1, 0) == 2 + 3 + 4 + 5 + 6);
update(&segtree, &lazy, 2, 4, 1, 0, 7 - 1, 0);
assert(query(&segtree, &lazy, 1, 5, 0, 7 - 1, 0) == 2 + 4 + 5 + 6 + 6);
update(&segtree, &lazy, 0, 6, -2, 0, 7 - 1, 0);
assert(query(&segtree, &lazy, 0, 4, 0, 7 - 1, 0) == -1 + 0 + 2 + 3 + 4);
}
/**
* @brief Main function
*
* @return 0 on exit
*/
int main() {
test(); // run self-test implementations
std::cout << "Enter number of elements: ";
uint64_t n = 0;
std::cin >> n;
auto max = static_cast<uint64_t>(2 * pow(2, ceil(log2(n))) - 1);
std::vector<int64_t> arr(n), lazy(max), segtree(max);
int choice = 0;
std::cout << "\nDo you wish to enter each number?:\n"
"1: Yes\n"
"0: No (default initialize them to 0)\n";
std::cin >> choice;
if (choice == 1) {
std::cout << "Enter " << n << " numbers:\n";
for (int i = 1; i <= n; i++) {
std::cout << i << ": ";
std::cin >> arr[i];
}
}
ConsTree(arr, &segtree, 0, n - 1, 0);
do {
std::cout << "\nMake your choice:\n"
"1: Range update (input)\n"
"2: Range query (output)\n"
"0: Exit\n";
std::cin >> choice;
if (choice == 1) {
std::cout << "Enter 1-indexed lower bound, upper bound & value:\n";
uint64_t p = 1, q = 1, v = 0;
std::cin >> p >> q >> v;
update(&segtree, &lazy, p - 1, q - 1, v, 0, n - 1, 0);
} else if (choice == 2) {
std::cout << "Enter 1-indexed lower bound & upper bound:\n";
uint64_t p = 1, q = 1;
std::cin >> p >> q;
std::cout << query(&segtree, &lazy, p - 1, q - 1, 0, n - 1, 0);
std::cout << "\n";
}
} while (choice > 0);
return 0;
}
@@ -0,0 +1,105 @@
/**
* @file sparse_table.cpp
* @brief Implementation of [Sparse
* Table](https://en.wikipedia.org/wiki/Range_minimum_query) data structure
*
* @details
* Sparse Table is a data structure, that allows answering range queries.
* It can answer most range queries in O(logn), but its true power is answering
* range minimum queries or equivalent range maximum queries). For those queries
* it can compute the answer in O(1) time.
*
* * Running Time Complexity \n
* * Build : O(NlogN) \n
* * Range Query : O(1) \n
*/
#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>
/**
* @namespace range_queries
* @brief Range Queries algorithms
*/
namespace range_queries {
/**
* @namespace sparse_table
* @brief Range queries using sparse-tables
*/
namespace sparse_table {
/**
* This function precomputes intial log table for further use.
* @param n value of the size of the input array
* @return corresponding vector of the log table
*/
template <typename T>
std::vector<T> computeLogs(const std::vector<T>& A) {
int n = A.size();
std::vector<T> logs(n);
logs[1] = 0;
for (int i = 2; i < n; i++) {
logs[i] = logs[i / 2] + 1;
}
return logs;
}
/**
* This functions builds the primary data structure sparse table
* @param n value of the size of the input array
* @param A array of the input integers
* @param logs array of the log table
* @return created sparse table data structure
*/
template <typename T>
std::vector<std::vector<T> > buildTable(const std::vector<T>& A,
const std::vector<T>& logs) {
int n = A.size();
std::vector<std::vector<T> > table(20, std::vector<T>(n + 5, 0));
int curLen = 0;
for (int i = 0; i <= logs[n]; i++) {
curLen = 1 << i;
for (int j = 0; j + curLen < n; j++) {
if (curLen == 1) {
table[i][j] = A[j];
} else {
table[i][j] =
std::min(table[i - 1][j], table[i - 1][j + curLen / 2]);
}
}
}
return table;
}
/**
* This function is the query function to get the range minimum value
* @param beg beginning index of the query range
* @param end ending index of the query range
* @param logs array of the log table
* @param table sparse table data structure for the input array
* @return minimum value for the [beg, end] range for the input array
*/
template <typename T>
int getMinimum(int beg, int end, const std::vector<T>& logs,
const std::vector<std::vector<T> >& table) {
int p = logs[end - beg + 1];
int pLen = 1 << p;
return std::min(table[p][beg], table[p][end - pLen + 1]);
}
} // namespace sparse_table
} // namespace range_queries
/**
* Main function
*/
int main() {
std::vector<int> A{1, 2, 0, 3, 9};
std::vector<int> logs = range_queries::sparse_table::computeLogs(A);
std::vector<std::vector<int> > table =
range_queries::sparse_table::buildTable(A, logs);
assert(range_queries::sparse_table::getMinimum(0, 0, logs, table) == 1);
assert(range_queries::sparse_table::getMinimum(0, 4, logs, table) == 0);
assert(range_queries::sparse_table::getMinimum(2, 4, logs, table) == 0);
return 0;
}