Added function documentation

TODO: Data member documentation.
This commit is contained in:
Aniruthan R
2020-08-14 01:17:47 +05:30
committed by GitHub
parent dfddc819c4
commit f4504cac44

View File

@@ -1,4 +1,4 @@
/*
/**
* @file heavy_ligt_decomposition.cpp
* @brief Heavy-Light Decomposition implementation
* @author: [Aniruthan R](https://github.com/aneee004)
@@ -6,7 +6,7 @@
* @CodeChef: aneee004
* @CodeForces: aneee
*
* TODO: Support edge weight quereis, by storing the edge weight value in it's child
* 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
*/
@@ -53,13 +53,27 @@
* Sample I/O at the bottom.
*/
/* A Basic Tree, which supports binary lifting */
/**
* A Basic Tree, which supports binary lifting
*/
template<typename X>
class Tree {
/*
* 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
*/
private:
std::vector<std::list<int>> t_adj;
const int t_nodes, t_maxlift;
@@ -68,13 +82,12 @@ private:
int t_root;
std::vector<X> t_val;
template<typename T> friend class HLD;
/* Defaults:
* t_node indexing are zero based
* t_root is 0
* depth of root_node is 0
*/
/**
* Utility function to compute sub-tree sizes
* @param u, current dfs node
* @param p, the parent of node @param u
*/
void dfs_size(int u, int p = -1) {
for(const int& v : t_adj[u]) {
if(v^p) {
@@ -84,6 +97,11 @@ private:
}
}
/**
* Utility function to populate the t_par vector
* @param u, current dfs node
* @param p, the parent of node @param u
*/
void dfs_lca(int u, int p = -1) {
t_par[u][0] = p;
if(p != -1) t_depth[u] = 1 + t_depth[p];
@@ -101,6 +119,10 @@ private:
}
public:
/**
* Class parameterized constructor. Resizes the and initilizes 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 */
@@ -111,35 +133,51 @@ public:
t_size.assign(t_nodes, 1);
t_val.resize(t_nodes);
}
/**
* 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
*/
void add_edge(const int u, const int v) {
t_adj[u].push_back(v);
t_adj[v].push_back(u);
}
/**
* Set the root for the tree
* @param new_root, the new root
*/
void change_root(int new_root) {
t_root = new_root;
}
/**
* 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
*/
void set_node_val(std::vector<X> node_val) {
assert(static_cast<int>(node_val.size()) == t_nodes);
t_val = node_val;
}
/* Initialization, sizes and parents */
/**
* 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
*/
void init() {
assert(t_nodes > 0);
dfs_size(t_root);
dfs_lca(t_root);
}
/* Supports:
* lift :- lift a node k units up the tree
* kth_ancestor :- returns the kth ancestor
* lca :- returns the least common ancestor
/**
* The function lifts a node, k units up the tree
* @param p, a pointer to the variable that stores the node id
* @param dist, the distance to move up the tree
* @return, void. The node if, after lifting is stored in the address pointed by @param p.
*/
/* in place lift */
void lift(int* const p, int dist) {
for(int k = 0; k < t_maxlift; k++) {
if(*p == -1) return;
@@ -150,13 +188,23 @@ public:
}
}
/* returns the kth ancestor */
/**
* 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
* @return, the kth ancestor of node @param p
*/
int kth_ancestor(int p, const int& dist) {
lift(&p, dist);
return p;
}
/* returns the least common ancestor */
/**
* The function returns the least common ancestor of two nodes
* @param a, node id_1
* @param b, node id_2
* @return, the least common ancestor of node @param a, and node @param 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]) {
@@ -176,18 +224,14 @@ public:
}
};
/* Segment Tree, to store heavy chains */
/**
* Segment Tree, to store heavy chains
*/
template<typename X>
class SG {
private:
/* Change the sret_init, based on requirement:
* Sum Query: 0 (Default)
* XOR Query: 0 (Default)
* Min Query: Infinity
* Max Query: -Infinity
*/
/* Everything here is private,
/**
* Everything here is private,
* and can only be acced thought the methods,
* in the derived class (HLD).
*/
@@ -196,23 +240,41 @@ private:
int s_size;
template<typename T> friend class HLD;
X sret_init = 0;
/**
* Function that specifies the type of operation involved when segments are combined
* @param lhs, the left segment
* @param rhs, the right segment
* @return, the combined result
*/
X combine(X lhs, X rhs) {
return lhs + rhs;
}
/* Construction requires a size */
/**
* Class parameterized constructor. Resizes the and initilizes the data members.
* @param nodes, the total number of nodes in the tree
*/
explicit SG(int size) {
s_size = size;
s_tree.assign(2*s_size, 0ll);
}
/* update a segment tree node value */
/**
* Update the value at a node
* @param p, the node to be udpated
* @param v, the update value
*/
void update(int p, X v) {
for(p += s_size; p > 0; p >>= 1) s_tree[p] += v;
}
/* make a segment tree range query */
/**
* 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
*/
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) {
@@ -221,20 +283,35 @@ private:
}
return combine(lhs, rhs);
}
void set_sret_init(int new_sret_init) {
/**
* Set the initialization for the query data type, based on requirement
* 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;
}
};
/* The Heavy-Light Decomposition class */
/**
* The Heavy-Light Decomposition class
*/
template<typename X>
class HLD : public Tree<X>, public SG<X> {
private:
int label;
std::vector<int> h_label, h_heavychlid, h_parent;
/* Utility functions */
/**
* 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 @param u
*/
void dfs_hc(int u, int p = -1) {
int hc_size = -1, hc_id = -1;
for(const int& v : Tree<X>::t_adj[u]) {
@@ -248,7 +325,12 @@ private:
}
h_heavychlid[u] = hc_id;
}
/**
* Utility function to assign highest parent that can be reached though heavy chains
* @param u, current dfs node
* @param p, the parent of node @param u
*/
void dfs_par(int u, int p = -1) {
if(h_heavychlid[u] != -1) {
h_parent[h_heavychlid[u]] = h_parent[u];
@@ -260,7 +342,12 @@ private:
}
}
}
/**
* 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 @param u
*/
void dfs_labels(int u, int p = -1) {
h_label[u] = label++;
if(h_heavychlid[u] != -1) dfs_labels(h_heavychlid[u], u);
@@ -270,7 +357,14 @@ private:
}
}
}
/**
* 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
* @return, 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);
@@ -287,6 +381,10 @@ private:
return ret;
}
public:
/**
* Class parameterized constructor. Resizes the and initilizes the data members.
* @param nodes, the total number of nodes in the tree
*/
explicit HLD<X>(int nodes) : Tree<X>(nodes), SG<X>(nodes) {
/* Initialization and resize vectors */
label = 0;
@@ -296,7 +394,10 @@ public:
iota(h_parent.begin(), h_parent.end(), 0);
}
/* Initialization */
/**
* 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
*/
void init() {
Tree<X>::init();
@@ -315,16 +416,23 @@ public:
}
}
/* Changes the value at node, to val */
/**
* 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
*/
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;
}
/*
* return the (sum) of node values in,
* the simple path from a, to b
/**
* 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)
* @return the sum of node values in the simple path from @param a to @param b
*/
X query(int a, int b) {
int lc = Tree<X>::lca(a, b);
@@ -336,6 +444,9 @@ public:
}
};
/**
* Test implementaions
*/
void test_1() {
std::cout << "Test 1:\n";
/* Test details */
@@ -427,11 +538,16 @@ void test_2() {
}
}
}
/**
* Main funciton
*/
int main() {
test_1();
test_2();
return 0;
}
/*
* Sample Output 1:
* 11