李超线段树
李超线段树
概念
李超线段树是巨佬李超发明的一种可以求函数定点最值的线段树,又名李超树。代码简短,思想简明,用途广泛。
问题
李超线段树是用来解决类似于这种问题 题目传送门
要求在平面直角坐标系下维护两个操作:
- 在平面上加入一条线段。记第
条被插入的线段的标号为 。 - 给定一个数
,询问与直线 相交的线段中,交点纵坐标最大的线段的编号。
思路
李超线段树的结构和普通线段树一样的,只是它每个节点存的是该区间优势最大的线段
区间
插入操作
对于一个区间,它被线段覆盖的情况可以分为六种:
1、线段覆盖的区间和该区间不相交,直接返回
2、线段覆盖部分该区间,递归到左右子区间继续处理;
3、线段完全覆盖该区间: 线段在两个端点处值均比之前保存的优势线段更大,则替换优势线段,返回;
4、线段在两个端点处的值均比之前的保存的优势线段小,则返回
5、线段在两个端点处与之前保存的优势线段比较,如果在中点处更优,则更新优势线段(交换两 线段);
6、然后再判断左右端点处的值,如果该侧优势线段不占优势,则递归该侧。
查询
它维护的是区间中点处的最大(最小)值的线段,采用 标记永久化。 所谓标记永久化是指标记不下传,查询时,需要由该节点及其所有祖先的标记综合起来才能得出答案。
因为李超树每个区间保存的只是当前区间的中点的最大值,所以它的标记是不能下传的,下传了反而不 准确。
每个区间保存的只是区间 处占优势的线段的编号。(另有一个线段的数组,记录了每条线段的 斜率和截距)。优势线段仅仅只是中点更优,并不是处处占优,也不是端点占优。
适合 单点查询,查询时必须要将叶节点的所有祖先的优势线段在该处进行比较。
例题
版题。 可以感性理解一下
#include <bits/stdc++.h>
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
#define lp p<<1
#define rp p<<1|1
using namespace std;
const int N = 1e5 + 5 , mod = 39989 , my = 1000000000;
const double eps = 1e-8;
int l[N << 3] , r[N << 3] , tr[N << 3] , tot;
double k[N] , b[N];
inline int dcmp(double x) {
return fabs(x) <= eps ? 0 : x < 0 ? -1 : 1;
// fabs (x) <= eps
}
double f (int x , int p) { return 1.0 * k[x] * p + b[x]; }
bool pd (int x , int y , int p) {
double fx = f(x , p) , fy = f (y , p);
return fabs (fy - fx) >= eps ? fx < fy : x > y;
// return dcmp(fx-fy) ? fx < fy : x > y;
}
void build (int p , int ll , int rr) {
l[p] = ll , r[p] = rr;
if (ll == rr) return;
int mid = (ll + rr) >> 1;
build (lp , ll , mid);
build (rp , mid + 1 , rr);
}
void change (int p , int ll , int rr , int x) {
if (r[p] < ll || rr < l[p]) return;
if (ll <= l[p] && r[p] <= rr) {
if (pd (x , tr[p] , l[p]) && pd (x , tr[p] , r[p])) return;
if (pd (tr[p] , x , l[p]) && pd (tr[p] , x , r[p])) {
tr[p] = x;
return;
}
int mid = (l[p] + r[p]) >> 1;
if (pd (tr[p] , x , mid)) swap (tr[p] , x);
if (pd (tr[p] , x , l[p])) change (lp , l[p] , r[p] , x);
else change (rp , l[p] , r[p] , x);
return;
}
change (lp , ll , rr , x) , change (rp , ll , rr , x);
}
int query (int p , int x) {
if (x < l[p] || r[p] < x) return 0;
if (l[p] == r[p] && l[p] == x) return tr[p];
int mid = (l[p] + r[p]) >> 1 , res = 0;
if (x <= mid) res = query (lp , x);
else res = query (rp , x);
// res = x <= mid ? query(lp,x) : query(rp,x);
if (pd (res , tr[p] , x)) res = tr[p];
return res;
}
int main () {
int T;
scanf ("%d" , &T);
int op , x , lst = -1 , y , xx , yy;
build (1 , 1 , 40000);
while (T --) {
scanf ("%d" , &op);
if (!op) {
scanf ("%d" , &x);
x = (x + lst + mod) % mod + 1;
// cout << x << "\n";
lst = query (1 , x);
printf ("%d\n" , lst);
lst --;
// if (lst) lst --;
}
else {
scanf ("%d%d%d%d" , &x , &y , &xx , &yy);
x = (x + lst + mod) % mod + 1 , xx = (xx + lst + mod) % mod + 1;
y = (1ll * y + lst + my) % my + 1 , yy = (1ll * yy + lst + my) % my + 1;
if (xx < x) swap (xx , x) , swap (yy , y);
tot++;
if (x == xx) k[tot] = 0 , b[tot] = max (y , yy);
else k[tot] = (double)(yy - y) / (xx - x) , b[tot] = yy - k[tot] * xx;
change (1 , x , xx , tot);
}
}
return 0;
}
// 785#include <bits/stdc++.h>
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
#define lp p<<1
#define rp p<<1|1
using namespace std;
const int N = 1e5 + 5 , mod = 39989 , my = 1000000000;
const double eps = 1e-8;
int l[N << 3] , r[N << 3] , tr[N << 3] , tot;
double k[N] , b[N];
inline int dcmp(double x) {
return fabs(x) <= eps ? 0 : x < 0 ? -1 : 1;
// fabs (x) <= eps
}
double f (int x , int p) { return 1.0 * k[x] * p + b[x]; }
bool pd (int x , int y , int p) {
double fx = f(x , p) , fy = f (y , p);
// return fabs (fy - fx) >= eps ? fx < fy : x > y;
// return dcmp(fx-fy) ? fx < fy : x > y;
return fx <= fy;
}
void build (int p , int ll , int rr) {
l[p] = ll , r[p] = rr;
if (ll == rr) return;
int mid = (ll + rr) >> 1;
build (lp , ll , mid);
build (rp , mid + 1 , rr);
}
void change (int p , int ll , int rr , int x) {
if (r[p] < ll || rr < l[p]) return;
if (ll <= l[p] && r[p] <= rr) {
if (pd (x , tr[p] , l[p]) && pd (x , tr[p] , r[p])) return;
if (pd (tr[p] , x , l[p]) && pd (tr[p] , x , r[p])) {
tr[p] = x;
return;
}
int mid = (l[p] + r[p]) >> 1;
if (pd (tr[p] , x , mid)) swap (tr[p] , x);
if (pd (tr[p] , x , l[p])) change (lp , l[p] , r[p] , x);
else change (rp , l[p] , r[p] , x);
return;
}
change (lp , ll , rr , x) , change (rp , ll , rr , x);
}
int query (int p , int x) {
if (x < l[p] || r[p] < x) return 0;
if (l[p] == r[p] && l[p] == x) return tr[p];
int mid = (l[p] + r[p]) >> 1 , res = 0;
if (x <= mid) res = query (lp , x);
else res = query (rp , x);
// res = x <= mid ? query(lp,x) : query(rp,x);
if (pd (res , tr[p] , x)) res = tr[p];
return res;
}
int main () {
int T;
scanf ("%d" , &T);
int op , x , lst = -1 , y , xx , yy;
build (1 , 1 , 40000);
while (T --) {
scanf ("%d" , &op);
if (!op) {
scanf ("%d" , &x);
x = (x + lst + mod) % mod + 1;
// cout << x << "\n";
lst = query (1 , x);
printf ("%d\n" , lst);
lst --;
// if (lst) lst --;
}
else {
scanf ("%d%d%d%d" , &x , &y , &xx , &yy);
x = (x + lst + mod) % mod + 1 , xx = (xx + lst + mod) % mod + 1;
y = (1ll * y + lst + my) % my + 1 , yy = (1ll * yy + lst + my) % my + 1;
if (xx < x) swap (xx , x) , swap (yy , y);
tot++;
if (x == xx) k[tot] = 0 , b[tot] = max (y , yy);
else k[tot] = (double)(yy - y) / (xx - x) , b[tot] = yy - k[tot] * xx;
change (1 , x , xx , tot);
}
}
return 0;
}
一般的斜率优化也可以用李超线段树来写
如果人生会有很长,愿有你的荣耀永不散场
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析