李超线段树
直线型
先看一种题型:
- 给出
,加入一条 的直线。 - 给出
, 查询与 相交的直线中纵坐标最大/最小值。
似乎是一种区间修改,单点查询?考虑使用线段树维护。
但对于不同的
这种情况下就需要使用另外一种线段树——李超线段树。
基本构成
在每个节点存储一组
但注意我这里的
可以使用动态开点来优化。
操作二
和传统线段树差别不大。由于是永久化标记的写法,只需要把从根节点到
为了避免查询无效的节点,如果当前节点没法继续走下去(即对应节点的
操作一
假设当前节点为
- 如果当前节点没有存储任何的
(可以采取给 初始化为极大/极小值来解决),则直接赋值即可。 - 否则,看对于
时,当前节点和修改的 那种情况更优,将更优的存储在节点 上,更劣的作为修改的 ,注意不是直接舍弃。这时有三种情况。 ,那么无论如何,也不可能用手上的 来更新答案,直接退出即可。 ,如果是求最大值,则去试图更新 ,否则去试图更新 。 ,如果是求最小值,则去试图更新 ,否则去试图更新 。
复杂度
加上上面所说的所有优化后,可以发现每条线段最多影响
但查询仍有可能跑满
线段型
如果加入的不是直线而是一条在
其实和上面差不多,但你需要找到那条线段对应每个的区间,再在这个区间内做一下直线型修改即可。
时间负杂度和空间复杂度都比上面多乘一个
例题
点击查看代码
// https://vjudge.d0j1a1701.cc/problem/Yosupo-segment_add_get_min #include <bits/stdc++.h> //#define _1 (__int128)1 //#define int long long using namespace std; using ll = long long; void FileIO (const string s) { freopen(string(s + ".in").c_str(), "r", stdin); freopen(string(s + ".out").c_str(), "w", stdout); } const int P = 1e9; const ll INF = 4e18; struct SegTree { int ls, rs, k; ll b = INF; } ; int n, q, ndcnt = 1, op, x, l, r; ll ans, y, b; vector<SegTree> tr; int ls (int id) { if (!tr[id].ls) tr.push_back({}), tr[id].ls = ++ndcnt; return tr[id].ls; } int rs (int id) { if (!tr[id].rs) tr.push_back({}), tr[id].rs = ++ndcnt; return tr[id].rs; } void modify (int id, int l, int r, int k, ll b, int x, int y) { if (l > y || r <= x || b == INF) return ; if (l >= x && r <= y + 1 && tr[id].b == INF) { tr[id].k = k, tr[id].b = b; return ; } int mid = l + (r - l) / 2; if (l >= x && r <= y + 1 && 1ll * tr[id].k * mid + tr[id].b > 1ll * k * mid + b) swap(tr[id].k, k), swap(tr[id].b, b); if ((tr[id].k == k && tr[id].b <= b) || l + 1 == r) return ; if (!(l >= x && r <= y + 1) || tr[id].k < k) modify(ls(id), l, mid, k, b, x, y); if (!(l >= x && r <= y + 1) || tr[id].k > k) modify(rs(id), mid, r, k, b, x, y); } void Query (int x) { int id = 1, l = -P, r = P + 1; while (1) { ans = min(ans, 1ll * tr[id].k * x + tr[id].b); int mid = l + (r - l) / 2; if (mid > x && tr[id].ls) id = ls(id), r = mid; else if (mid <= x && tr[id].rs) id = rs(id), l = mid; else break; } } signed main () { ios::sync_with_stdio(0), cin.tie(0); // FileIO(""); cin >> n >> q, tr.push_back({}), tr.push_back({}); for (int i = 1, k, x, y; i <= n; i++) { cin >> x >> y >> k >> b; modify(1, -P, P + 1, k, b, x, y - 1); } while (q--) { cin >> op >> l; if (op) { ans = INF; Query(l); if (ans == INF) { cout << "INFINITY\n"; continue; } cout << ans << '\n'; } else { cin >> r >> x >> y; modify(1, -P, P + 1, x, y, l, r - 1); } } return 0; }
本文作者:wnsyou の blog
本文链接:https://www.cnblogs.com/wnsyou-blog/p/18400703/li_chao_tree
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步