李超线段树
线段树是一类维护点的操作的数据结构,当我们处理线段问题时,同样可以将其归约到线段覆盖的一系列点上。这就是李超线段树的核心思想。
区间
朴素的做法是求出
假设对于一个区间,我们已经维护了每个点
-
此时只需要比较
和 即可。 -
不妨设
,此时两线段有一交点 ,且 时, ; 时, 。这提示我们可以采取分治的思想。如果在某一半中,一条直线被另一条直线完全偏序,可以给这一半打上标记,递归另一侧。具体地,对于一段区间
,我们取得 两条直线在 处的取值,如果 ,则认为 是这个区间内的最优直线,然后判断 和 两点的取值决定分治哪边。正确性显然。
查询时将当前区间,左子区间,右子区间三个区间的最优线段在
值得注意的是,李超线段树也可以方便的解决线段的问题。因为在建线段树时,横坐标的左右边界已经被标定了,虽然说是直线,其实本质上也是线段,只要在对应的位置上插入即可。
贴一份典题代码:
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 0; char c = getchar();
for (; !isdigit(c); c = getchar()) f |= c == '-';
for (; isdigit(c); c = getchar()) x = x * 10 + (c & 15);
return f ? -x : x;
}
const int N = 1e5 + 5, P1 = 39989, P2 = 1e9;
const double eps = 1e-5;
int n, rnk[N << 4];
struct node {
double k, b;
} p[N];
int cnt;
inline void add(double x0, double y0, double x1, double y1) {
++cnt;
if (abs(x1 - x0) < eps) p[cnt].k = 0, p[cnt].b = max(y0, y1);
else p[cnt].k = 1. * (y1 - y0) / (x1 - x0), p[cnt].b = y0 - p[cnt].k * x0;
}
double y(int id, int x) { return p[id].b + p[id].k * x; }
// cmp_double: 1 -> x > y, -1 -> x < y, 0 -> x = y
inline int cmp_double(double x, double y) {
if (x - y > eps) return 1;
if (x - y < -eps) return -1;
return 0;
}
pair<double, int> max(pair<double, int> x, pair<double, int> y) {
int c = cmp_double(x.first, y.first);
if (c == 1) return x;
if (c == -1) return y;
return x.second < y.second ? x : y;
}
pair<double, int> query(int p, int l, int r, int x) {
if (r < x || l > x) return {0, 0};
int mid = (l + r) >> 1;
double res = y(rnk[p], x);
if (l == r) return {res, rnk[p]};
return max({res, rnk[p]}, max(query(p << 1, l, mid, x), query(p << 1 | 1, mid + 1, r, x)));
}
void modify(int p, int l, int r, int id) {
int &bef = rnk[p], mid = (l + r) >> 1;
if (cmp_double(y(id, mid), y(bef, mid)) == 1) swap(id, bef);
// if (l == r) return;
int bl = cmp_double(y(id, l), y(bef, l)), br = cmp_double(y(id, r), y(bef, r));
if (bl == 1 || (!bl && id < bef)) modify(p << 1, l, mid, id);
if (br == 1 || (!br && id < bef)) modify(p << 1 | 1, mid + 1, r, id);
}
void update(int p, int l, int r, int al, int ar, int id) {
if (al <= l && r <= ar) {
modify(p, l, r, id);
return;
}
int mid = (l + r) >> 1;
if (al <= mid) update(p << 1, l, mid, al, ar, id);
if (mid < ar) update(p << 1 | 1, mid + 1, r, al, ar, id);
}
int main() {
int n = read(), las = 0;
while (n--) {
int opt = read();
if (opt == 0) {
printf("%d\n", las = query(1, 1, P1, (read() + las - 1 + P1) % P1 + 1).second);
}
else {
int x0 = (read() + las - 1 + P1) % P1 + 1, y0 = (read() + las - 1 + P2) % P2 + 1,
x1 = (read() + las - 1 + P1) % P1 + 1, y1 = (read() + las - 1 + P2) % P2 + 1;
if (x0 > x1) swap(x0, x1), swap(y0, y1);
add(x0, y0, x1, y1);
update(1, 1, P1, x0, x1, cnt);
}
}
return 0;
}
代码略显复杂的原因是这题还要求了编号最小,实际实现时一般不会做此要求。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探