李超线段树

article/2025/9/30 2:04:23

什么是李超线段树

先以一个问题引入:
在平面上有两种操作(强制在线):
插入一条表达式为 L : y = k*x+b 的直线,给出 k ,b 。
给出 t,求当前所有直线中与直线 x = t 交点的纵坐标最大是多少
直线取 max 应该是得到一个下凸包,像这样(黑色的):
在这里插入图片描述

李超线段树是什么?线段树,当然是要维护区间 [L , R] 的一个信息,李超线段树维护的就是中点处最高的直线
比如下图,区间 [ 0 , 4 ] 的中点 x = 2 的最高直线为红色直线
在这里插入图片描述

利用这一信息,可以在 O(log n ) 的复杂度内插入直线、查询与 x = t 相交的最高点。(这里的 n 指的是查询的 t 的值域范围)

实现李超线段树

  • 插入直线
    李超线段树有一个非常重要的思想:标记永久化。
    于是我们可以这样实现李超线段树,假如要把直线 L 插入某一区间:
  1. 如果当前区间还没有直线标记(不代表没有直线覆盖,因为标记永久化),那么就把标记记为L ;
  2. 如果 L 完全覆盖原有线段,像这样(蓝色是 L,红色是原有线段):
    在这里插入图片描述
    那么 x = t 与 [L,R] 的交点一定高于原线段,于是把原有线段直接替换为 L ;
  3. 如果 L 被原有线段完全覆盖(就是2.反过来),那么 L 一定没有原有线段优;这样的话就舍弃 L 不做任何更新。
  4. 如果 L 和原有线段在 [ L , R ] 中有交点,像这样:
    在这里插入图片描述
    这种情况就无法确定 与哪条直线交点更高。根据李超线段树维护的信息,我们需要在两条直线中取与 x = mid 交点更高的一条直线作为标记(也就是上图的黄色直线,剩下的那条就是绿色直线)。再判断交点:如果在左区间,那么绿色直线在左区间可能比黄色直线高,然后就尝试把绿色直线往左区间插入;右区间类似。
  • 查询
    由于是标记永久化,查询就比较类似于标记永久化的线段树。比如查询 x = pos 的最高交点,那么就要从线段树一层层递归,直到递归到 [pos , pos] 这个区间——把每个区间的最优线段的交点取 max。

  • 比如这样:
    在这里插入图片描述
    (下面的黄、绿、蓝、橙色线段表现了递归区间的过程)
    P4254 [JSOI2008]Blue Mary开公司

#include<bits/stdc++.h>
#include <unordered_map>
using namespace std;
template<class...Args>
void debug(Args... args) {//Parameter packauto tmp = { (cout << args << ' ', 0)... };cout << "\n";
}
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>pll;
typedef pair<int, int>pii;
const ll N = 1e6 + 5;
const ll MOD = 998244353;
const ll INF = 0x7fffffff;
const double eps = 1e-12;
struct line {//线段double k, b;//斜率和与y轴int l, r;int flag;
}tree[N<<2];//线段树的定义
double calc(line a, int pos) { return a.k * pos + a.b; }//计算某条线段在某一个横坐标的纵坐标值
int cross(line a, line b) { return floor((a.b - b.b) / (b.k - a.k)); }//求两条线段交点的横坐标
void build(int root, int l, int r){tree[root]={0,0,1,50000,0};if (l == r)return;int mid = (l + r) >> 1;build(root << 1, l, mid); build(root << 1 | 1, mid + 1, r);
}
void modify(int root, int l, int r, line k){if (k.l <= l && r <= k.r) {//1.这个区间内没有记录有过优势线段:直接把这个区间的势线段修改为这条线段if (!tree[root].flag)tree[root] = k, tree[root].flag = 1;//2.新线段完全覆盖了之前记录的线段:优势线段为新线段,直接赋值替换else if (calc(k, l) - calc(tree[root], l) > eps && calc(k, r) - calc(tree[root], r) > eps)tree[root] = k;//3.区间内线段有交点的情况:判断哪根线段为优势线段,把区间记录的值给修改一下,然后把短的那一半递归处理else if (calc(k, l) - calc(tree[root], l) > eps || calc(k, r) - calc(tree[root], r) > eps) {int mid = (l + r) >> 1;//取出区间的中点if (calc(k, mid) - calc(tree[root], mid) > eps) {//与中点交点更高的一条直线作为优势线段line tmp = k; k = tree[root]; tree[root] = tmp;}//交点在中点的左侧,此时老线可能比被标记的优势线段高需要修改if (mid - cross(k, tree[root]) > eps)modify(root << 1, l, mid, k);//交点在中点的右侧,同理需要修改右侧区间的优势线段else modify(root << 1 | 1, mid + 1, r, k);}}else {//要修改线段的区间没完全包含在某根节点的区间范围内int mid = (l + r) >> 1;if (k.l <= mid)modify(root << 1, l, mid, k);if (mid < k.r)modify(root << 1 | 1, mid + 1, r, k);}
}
double query(int root, int l, int r, int x){//由于是标记永久化,查询就比较类似于标记永久化的线段树//那么就要从线段树一层层递归,直到递归到某个点//每个区间的最优线段的交点取 maxif (l == r)return calc(tree[root], x);else {int mid = (l + r) >> 1;double ans = calc(tree[root], x);if (x <= mid)return max(ans, query(root << 1, l, mid, x));else return max(ans, query(root << 1 | 1, mid + 1, r, x));}
}
int main() {ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);int n;cin >> n;build(1, 1, 50000);for (int i = 1; i <= n; i++) {string op;cin >> op;if (op[0] == 'P') {double s, p;cin >> s >> p;line now; now.l = 1; now.r = 50000; now.k = p; now.b = s - p;modify(1, 1, 50000, now);}else {int x;cin >> x;cout << floor(query(1, 1, 50000, x) / 100) << "\n";}}return 0;
}

http://chatgpt.dhexx.cn/article/C9GlM1Kc.shtml

相关文章

线段树详解 (原理,实现与应用)

线段树详解 By 岩之痕 目录&#xff1a; 一&#xff1a;综述 二&#xff1a;原理 三&#xff1a;递归实现 四&#xff1a;非递归原理 五&#xff1a;非递归实现 六&#xff1a;线段树解题模型 七&#xff1a;扫描线 八&#xff1a;可持久化 (主席树) 九&#xff1a;练习题 一&a…

线段树详解(附图解,模板及经典例题分析)

1.基本概念 ​ 线段树是一种常用来维护 区间信息 \color{Maroon}区间信息 区间信息 的数据结构 ​ 可以在 O(logn) 的时间复杂度内实现单点修改、区间修改、区间查询&#xff08;区间求和&#xff0c;求区间的最大值&#xff0c;求区间的最小值&#xff09;等操作 ​ 如下图…

线段树详解

文章目录 1. 什么是线段树2. 线段树的用处3. 线段树的模板题4. 动态开点线段树5. 权值线段树5. 可持久化线段树 1. 什么是线段树 顾名思义 &#xff0c; 线段树是一棵二叉树 &#xff0c; 但不同的是这棵树的结点储存的值是一个数列中 [ l , r ] [l,r] [l,r] 的某个需要的值 …

超详解线段树(浅显易懂,几乎涵盖所有线段树类型讲解,匠心之作,图文并茂)

一&#xff0c;什么是线段树&#xff1f; 线段树是怎样的树形结构? 线段树是一种二叉搜索树&#xff0c;而二叉搜索树&#xff0c;首先满足二叉树&#xff0c;即每个结点最多有两颗子树&#xff0c;并且是一颗搜索树&#xff0c;我们要知道&#xff0c;线段树的每个结点都存储…

线段树 从入门到进阶(超清晰,简单易懂)

目录 第一部 概念引入第二部 简单&#xff08;无pushdown&#xff09;的线段树1、单点修改&#xff0c;区间查询2、区间修改&#xff0c;单点查询 第三部 进阶线段树第四部 乘法&#xff08;根号&#xff09;线段树1、乘法线段树2、根号线段树模板题与代码&#xff1a;单点修改…

Navicat Premium 数据库开发工具

官网地址&#xff1a;https://www.navicat.com.cn/ Navicat Premium 是一套数据库开发工具&#xff0c;让你从单一应用程序中同时连接 MySQL、MariaDB、MongoDB、SQL Server、Oracle、PostgreSQL 和 SQLite 数据库。它与 Amazon RDS、Amazon Aurora、Amazon Redshift、Microso…

Navicat 一款数据库开发软件

Navicat premium是一款数据库管理工具&#xff0c;非常好用&#xff0c;能复制表结构等&#xff0c;大大简化了后端开发的工程量&#xff0c;其软件示意图如下 Navicat Premium 15 已经发布了&#xff0c;但是价格昂贵&#xff0c;所以将 no-money version 的双手奉上。 使用…

Java学习中的数据库和数据库开发工具

一、数据库 1、数据库&#xff0c;通常是一个戒一组文件&#xff0c;保存了一些符合特定规格的数据&#xff0c;数据库对应的英询单词是DataBase&#xff0c;简称DB&#xff1b;数据库软件称为数据库管理系统&#xff0c;英文简称DBMS&#xff0c;全称为DataBase Management S…

五大主流数据库深度对比!数据库开发、管理看这篇就够了

开发数据库应用&#xff0c;选择一个好的数据库是非常重要的。目前&#xff0c;商品化的数据库管理系统以关系型数据库为主导产品&#xff0c; 技术比较成熟。面向对象的数据库管理系统技术先进&#xff0c;数据库易于开发、维护。无论是关系型数据库还是非关系型数据库&#x…

mysql数据库开发环境_MySQL数据库教程-环境与集成开发工具

MySQL数据库基础教程 数据库是存储数据与管理数据的基础,也是目前绝大多数动态网站开发所必备的基础知识。MySQL数据库教程系列文章主要以MySQL数据库管理系统为例对关系型数据库相关定义与操作等进行讲解。并结合php与PDO等讲解,为初学者提供快速学习PHP动态网站开发所需的知…

C/C++ MySQL数据库开发

C/C++ MySQL数据库开发笔记 MySQL启动方式手动启动MySQL数据库:命令行方式启动MySQL数据库:配置环境变量MySQL在Linux下安装MySQL的配置文件重要参数设置C/C++ 访问MySQL数据库(MySQL开发环境的配置(MySQL开发头文件和库文件)MySQL 数据库的连接MySQL 数据类型以及对应的 …

1、 数据库开发规范

第1章 数据库开发规范的制定 数据库设计步骤&#xff1a; 1、数据结构设计&#xff1a;逻辑设计-》物理设计 2、实际工作中&#xff1a;逻辑设计物理设计 3、物理设计&#xff1a;表名字段名字段类型 数据库设计几个规范&#xff1a; 数据库命名规范、数据库基本设计规范…

数据库开发-2-开发数据库的要点

Lec2-开发数据库的要点 1. 开发成功数据库应用的特点 需要理解数据库体系结构需要理解锁和并发控制特性&#xff1a;每个数据库都以不同的方式实现最重要的是不要把数据库当"黑盒" 要了解每一个数据库的体系结构和特征最常见的问题就是因为我们对数据库本身了解不足…

数据库开发(Sqlite)

1、数据库开发 1.1 数据与数据管理 什么是信息&#xff1f;   信息是指对现实世界存在方式或运动状态的反应。 什么是数据&#xff1f;   数据是指存储在某一媒体上&#xff0c;能够被识别的物理符号&#xff1b;   数据的概念在数据处理领域已经被大为拓宽&#xff0c…

《软件测试的艺术》第4章:测试用例的设计-白盒测试

写在前面&#xff1a;原书中包含白盒测试、黑盒测试、错误猜测、测试策略四个小节&#xff0c;涵盖内容较多&#xff0c;因此按章节拆分叙述。 《软件测试的艺术》&#xff1a; 白盒测试--语句覆盖 语句覆盖的用例设计原则&#xff1a;将程序中的每条语句至少执行一次。 白盒…

《软件测试的艺术》读后感 Or 读书笔记

《软件测试的艺术》读后感 Or 读书笔记 第一章 一次自评价测试第二章 软件测试的心理学和经济学第三章 代码检查、走查与评审第四章 测试用例的设计第五章 模块&#xff08;单元&#xff09;测试第六章 更高级别的测试第七章 可用性&#xff08;或用户体验&#xff09;测试第八…

软件测试的艺术 学习笔记

文章目录 4.2黑盒测试4.2.1 等价划分4.2.2 边界值分析4.2.3 因果图 4.3 错误猜测4.4 测试策略 5. 模块(单元&#xff09;测试5.1 测试用例设计5.2 增量测试5.3 自顶向下测试与自底向上测试5.3.1 自顶向下的测试5.3.2 自底向上的测试5.3.3 比较 5.4 执行测试 6 更高级别的测试6.…

《软件测试的艺术》第六章 更高级别的测试

《软件测试的艺术》第六章 更高级别的测试 6.0 前言软件开发过程模型 6.1 功能测试6.2 系统测试6.2.1 能力测试6.2.2 容量测试6.2.3 强度测试6.2.4 可用性测试6.2.5 安全性测试6.2.6 性能测试6.2.7 存储测试6.2.8 配置设置6.2.9 兼容性/转换测试6.2.10 安装测试6.2.11 可靠性测…

模块测试(单元测试)——软件测试的艺术

是大型程序测试的第一个步骤【大型程序即超过500条语句的程序】 了解 模块测试是对程序中的单个程序、子程序/过程进行测试的过程【并非对整个程序】&#xff1a; 关注点在较小单元&#xff0c;是一种管理组合的测试元素的手段减轻调试的难度&#xff0c;把错误定位到一个小…

《软件测试的艺术》第1章:一次自评价测试

写在前面&#xff1a; 相比于芯片验证&#xff0c;软件测试有着悠久的历史沉淀和更为完整的生态&#xff0c;和芯片验证在某些方面上几乎有着相同的思路和方法。因此从软件测试的视角出发&#xff0c;重新思考芯片验证的方方面面。第一个系列为《软件测试的艺术》学习。 第一…