线段树(动态开点)在解决 LeetCode 699 题中的妙用
2023-11-07 04:58:53
当算法问题与数据结构碰撞时,往往能迸发出精彩的火花。在 LeetCode 699 题《掉落的方块》中,线段树(动态开点)的巧妙运用就是如此。
699 题
给你一个大小为 n x n 的方格网,网格中每个格点要么为空,要么有一个方块。每个方块从网格的上方边缘掉落,如果它落到空格子中,则会停留在该格子中;否则,它会压在下方第一个不为空的格子上。
动态开点线段树的运用
要解决这个问题,我们需要一种数据结构来维护网格中方块的状态。线段树是一种完美的选择,因为它允许我们高效地更新和查询区间信息。
然而,在该问题中,网格的大小是动态的。方块的掉落会改变网格中非空的方格数,因此我们需要使用动态开点线段树。
动态开点线段树与普通线段树的不同之处在于,它允许在需要时动态创建新节点。这使我们能够在 O(log n) 时间内高效处理网格大小的变化。
两种动态开点线段树实现方式
动态开点线段树有两种常见的实现方式:
1. 标记传播法
此方法通过标记节点来表示需要创建的新节点。当需要更新包含标记的区间时,我们会递归地将标记向下传递,直到创建所有必要的节点。
2. 惰性传播法
此方法通过延迟更新来表示需要创建的新节点。当需要更新包含延迟更新的区间时,我们会立即创建所有必要的节点,然后执行实际的更新。
应用场景和示例代码
动态开点线段树在解决 LeetCode 699 题时,可以用来维护网格中每个列中非空方格的数量。当方块掉落时,我们可以使用线段树高效地更新相应的区间信息,从而得到方块的最终位置。
以下是使用标记传播法实现的动态开点线段树的部分示例代码:
struct SegmentTree {
vector<int> tree;
vector<int> lazy;
int size;
void init(int n) {
size = 1 << (int)ceil(log2(n));
tree.resize(2 * size);
lazy.resize(2 * size);
}
void update_range(int l, int r, int val) {
update_range(l, r, val, 1, 0, size - 1);
}
void update_range(int l, int r, int val, int node, int nl, int nr) {
propagate(node, nl, nr);
if (l <= nl && nr <= r) {
lazy[node] = val;
propagate(node, nl, nr);
return;
}
if (r < nl || l > nr) {
return;
}
int mid = (nl + nr) / 2;
update_range(l, r, val, 2 * node, nl, mid);
update_range(l, r, val, 2 * node + 1, mid + 1, nr);
tree[node] = merge(tree[2 * node], tree[2 * node + 1]);
}
int query(int l, int r) {
return query(l, r, 1, 0, size - 1);
}
int query(int l, int r, int node, int nl, int nr) {
propagate(node, nl, nr);
if (l <= nl && nr <= r) {
return tree[node];
}
if (r < nl || l > nr) {
return 0;
}
int mid = (nl + nr) / 2;
return merge(query(l, r, 2 * node, nl, mid), query(l, r, 2 * node + 1, mid + 1, nr));
}
private:
void propagate(int node, int nl, int nr) {
if (lazy[node] != 0) {
tree[node] = lazy[node] * (nr - nl + 1);
if (nl != nr) {
lazy[2 * node] = lazy[node];
lazy[2 * node + 1] = lazy[node];
}
lazy[node] = 0;
}
}
int merge(int a, int b) {
return a + b;
}
};
总结
线段树(动态开点)在解决 LeetCode 699 题中发挥了至关重要的作用。其高效地处理动态网格和区间更新的能力,使我们能够优雅地解决这个问题。无论是标记传播法还是惰性传播法,动态开点线段树都是算法工具箱中宝贵的工具。