李超线段树
定义
李超线段树是一种用于维护多条一次函数的线段树,可以在 \(O(\log n)\) 的复杂度内插入一条一次函数,\(O(\log n)\) 的复杂度内查询某个横坐标上的最值。
原理
考虑使用标记永久化的思想,对于每个节点储存在当前区间中 mid 的最值,这样查询时只需要对经过的每个节点取最值即可。

考虑更新完当前点的值后,对两条一次函数交叉的一侧继续递归修改,时间复杂度 \(O(\log n)\).
实现
代码截自 洛谷-P4097 李超线段树 / [HEOI2013] Segment
bool cmp(double a, double b) {return a - b >= 1e-8;}
void modify(int u, int l, int r, int p)
{
if(l == r) {if(cmp(calc(p, l), calc(t[u], l))) t[u] = p; return ;}
int mid = l + r >> 1;
if(cmp(calc(p, mid), calc(t[u], mid))) swap(t[u], p); // 插入的函数在 mid 优于原来的答案
if(!p) return ;
if(!cmp(k[p], k[t[u]])) modify(ls(u), l, mid, p); else modify(rs(u), mid + 1, r, p); // 往交叉的一侧递归
}
double query(int u, int l, int r, int p)
{
if(l == r) return t[u];
int mid = l + r >> 1, res;
if(p <= mid) res = query(ls(u), l, mid, p); else res = query(rs(u), mid + 1, r, p);
return cmp(calc(t[u], p), calc(res, p)) ? t[u] : res;
}
拓展
插入线段
通过在套一层线段树的区间修改操作,在 \(O(\log^2 n)\) 内完成一次插入。
void update(int u, int l, int r, int ql, int qr, int p)
{
if(ql <= l && r <= qr) return modify(u, l, r, p);
int mid = l + r >> 1;
if(ql <= mid) update(ls(u), l, mid, ql, qr, p);
if(qr > mid) update(rs(u), mid + 1, r, ql, qr, p);
}
李超线段树合并
与普通的线段树合并基本相同,但要注意的是如果两棵树都存在同一条直线,需要在其中一颗上进行一次插入操作插入另一棵树上的线段。
void merge(int &p, int &q, int l, int r)
{
if(!q) return ; if(!p) return tr[p = newnode()] = tr[q], del(q);
if(l != r)
{
int mid = l + r >> 1;
merge(tr[p].ls, tr[q].ls, l, mid), merge(tr[p].rs, tr[q].rs, mid + 1, r);
}
modify(p, l, r, tr[q].t), del(q);
}

浙公网安备 33010602011771号