2025.3.1 闲话:扫描线线段树空间问题
先看正常的线段树代码片段:
void add(int l,int r,int x,int p=1)
{
if(l<=tree[p].l&&tree[p].r<=r)
{
tree[p].dat+=x*tree[p].get_len();
tree[p].lazy+=x;
return;
}
spread(p)
int mid=tree[p].get_mid();
if(l<=mid) add(l,r,x,p<<1);
if(r>mid) add(l,r,x,p<<1|1);
update(p);
return;
}
可以看到,对于叶节点,我们不会执行 update(p),因为只要进入了叶节点,那么开头的判断条件一定成立,直接 return 了。
在这种情况下,线段树大小是 \(4n\)。
再来看扫描线所用线段树的代码片段:
inline void update(int p)
{
if(tree[p].cnt) tree[p].len=from[tree[p].r+1]-from[tree[p].l];
else tree[p].len=tree[p<<1].len+tree[p<<1|1].len;
return;
}
void Add(int l,int r,int x,int p)
{
if(l<=tree[p].l&&tree[p].r<=r)
tree[p].cnt+=x;
else
{
int mid=tree[p].l+tree[p].r>>1;
if(l<=mid) Add(l,r,x,p<<1);
if(r>mid) Add(l,r,x,p<<1|1);
}
update(p);
return;
}
可以看到,在这种情况下即使是叶节点也会执行 update 函数,而 update 可能访问当前节点的子节点(即使叶节点的子节点为空)。
而普通线段树的叶节点最大编号为 \(4n\),这里叶节点还要访问子节点,那么这里就不得不开 \(8n\) 的空间(或者可以对叶节点特判)。
\(n\) 表示元素数量,而扫描线中线段树所存元素数量为两倍矩形数量,所以前面的 \(n\) 其实是 \(2n\)。因此扫描线线段树空间应开 \(16n\)(真吓人)。
本文采用 「CC-BY-NC 4.0」 创作共享协议,转载请注明作者及出处,禁止商业使用。
作者:Jerrycyx,原文链接:https://www.cnblogs.com/jerrycyx/p/18755599

浙公网安备 33010602011771号