Chtholly tree, also known as "Chtholly Notion Tree," is a data structure designed by a Chinese student named "Chtholly_micromaker" and first publicized in a Codeforces tutorial. It is a type of segment tree that aims to optimize range query problems by reducing unnecessary updates.
One of the key features of Chtholly tree is its ability to handle both point updates and range queries in logarithmic time complexity. This is achieved by maintaining a balanced tree structure, similar to a AVL tree or a red-black tree, and using lazy propagation to defer updates to specific intervals.
The Chtholly tree also has the ability to split and merge intervals in logarithmic time, which makes it a suitable data structure for solving problems that involve dynamic intervals, such as online contests and dynamic programming. This feature also allows the Chtholly tree to handle a large number of operations with a small amount of memory, making it a memory-efficient alternative to other segment tree implementations.
The implementation of a Chtholly tree can be divided into three main parts: the tree structure, the lazy propagation mechanism, and the split and merge operations.
The tree structure of a Chtholly tree is similar to that of a binary search tree, where each node represents an interval and has left and right children that represent sub-intervals. Each node also stores additional information, such as the size of the interval, the maximum value within the interval, and the lazy tag, which is used to defer updates.
The lazy propagation mechanism is responsible for deferring updates to specific intervals. When an update operation is performed, the lazy tag of the affected nodes is set to indicate that an update is pending. When a query operation is performed, the lazy tag is propagated down the tree to ensure that the current state of the tree is up to date.
The split and merge operations are used to divide and combine intervals. When a split operation is performed, the interval is divided into two sub-intervals and the original interval is removed from the tree. When a merge operation is performed, two intervals are combined into a single interval and the original intervals are removed from the tree.
Here is an example of a Chtholly tree implemented in Rust:
use std::cmp::{max, min};
struct Node {
val: i32,
tag: i32,
lson: Option<Box<Node>>,
rson: Option<Box<Node>>,
}
impl Node {
fn new(val: i32) -> Self {
Node {
val,
tag: 0,
lson: None,
rson: None,
}
}
}
struct ChthollyTree {
root: Option<Box<Node>>,
siz: i32,
}
impl ChthollyTree {
fn new() -> Self {
ChthollyTree { root: None, siz: 0 }
}
fn insert(&mut self, val: i32) {
if self.siz == 0 {
self.root = Some(Box::new(Node::new(val)));
self.siz += 1;
return;
}
self.insert_helper(&mut self.root, val);
}
fn insert_helper(node: &mut Option<Box<Node>>, val: i32) {
let mut cur = node.take().unwrap();
if val <= cur.val {
if cur.lson.is_none() {
cur.lson = Some(Box::new(Node::new(val)));
self.siz += 1;
return;
}
self.insert_helper(&mut cur.lson, val);
} else {
if cur.rson.is_none() {
cur.rson = Some(Box::new(Node::new(val)));
self.siz += 1;
return;
}
self.insert_helper(&mut cur.rson, val);
}
cur.val = max(cur.val, val);
node.replace(cur);
}
fn erase(&mut self, val: i32) {
self.erase_helper(&mut self.root, val);
}
fn erase_helper(p: usize) {
if l[p] == r[p] {
l[p] = r[p] = 0;
val[p] = 0;
size[p] = 0;
tag[p] = 0;
pushup(p);
return;
}
pushdown(p);
let mid = (l[p] + r[p]) >> 1;
if val[p] <= mid {
erase_helper(ls[p]);
} else {
erase_helper(rs[p]);
}
pushup(p);
}
}
The erase_helper
function is a helper function that is used to delete a value from the Chtholly tree. It first checks if the left and right boundaries of the current node are the same, which means that the node is a leaf node. If it is a leaf node, it sets the left and right boundaries to 0, sets the value of the node to 0, sets the size of the node to 0, and sets the tag to 0. After this, it calls the pushup
function to update the parent node's information.
If the node is not a leaf node, it first calls the pushdown
function to push any pending tag updates to its children. It then calculates the middle of the current node's range using the formula mid = (l[p] + r[p]) >> 1
. If the value of the current node is less than or equal to the middle value, it recursively calls the erase_helper
function on the left child. Otherwise, it recursively calls the erase_helper
function on the right child. Finally, it calls the pushup
function to update the parent node's information.