李超线段树

定义

李超线段树是一种用于维护多条一次函数的线段树,可以在 \(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);
}

待续……

posted @ 2025-03-10 18:16  lfyszy  阅读(14)  评论(0)    收藏  举报