P4097 【模板】李超线段树 / [HEOI2013] Segment 题解

题意

有一个平面直角坐标系,总共 n n n 个操作,每个操作有两种:

  • 给定正整数 x 0 , y 0 , x 1 , y 1 x_0,y_0,x_1,y_1 x0,y0,x1,y1 表示一条线段的两个端点。你需要在平面上加入这一条线段,第 i i i 条被插入的线段的标号为 i i i
  • 给定正整数 k k k,问与直线 x = k x=k x=k​ 相交的线段中,交点的纵坐标最大的线段的编号。

解法

需要用到线段树的一个变体:李超线段树。我们将操作转化一下:

  • 加入一个一次函数,定义域为 [ l , r ] [l,r] [l,r](这个一次函数画出的线段,左端点横坐标为 l l l,右端点横坐标为 r r r);
  • 给定 k k k,在所有 l ≤ k ≤ r l\le k\le r lkr 的一次函数中,找到在 x = k x=k x=k 处取值最大的那个函数(意思就是在横坐标为 k k k、垂直于 y y y 轴的直线上,找到 y y y 坐标最高的交点)。

来看一个例子:

在这里插入图片描述

因为我们总是取 y y y 坐标最高的交点作为答案,所以真正取到答案的部分长这样:

在这里插入图片描述

  • 图中黑色部分即为答案。

那如何在线段树上维护这个东西呢?我们考虑线段树上被两条线段完全覆盖的一个部分为 [ l , r ] [l,r] [l,r] 的节点的情况:

在这里插入图片描述

  • 图中红蓝色为两个一次函数的图像、橙色为对答案有贡献的部分、深灰色为 m i d = ⌊ l + r 2 ⌋ mid=\lfloor\cfrac{l+r}{2}\rfloor mid=2l+r、浅灰色为转折点。李超线段树的每个节点都会维护当前区间中优势最大的线段(图中红色的线段),因而李超线段树需要标记永久化

其中,红色线段(记为 g g g)先被加入,显然整个线段在当时就是最优的;蓝色线段(记为 f f f)然后被加入。此时深红色的部分没有受到影响, g g g 仍然优势最大;但浅蓝色部分答案改变。我们将其分为两个子区间(而 m i d mid mid 左右的区间另称为左/右区间),可以发现一定有一个子区间被左或右区间完全包含(浅蓝色被右区间包含),即在两条线段中,肯定有一条线段只可能成为左或右区间的答案 f f f 只可能成为右区间最优的线段)。

我们不妨令,在 m i d mid mid f f f 不如 g g g 优(反之交换两条线段即可),则:

  • 若在左端点处 f f f 更优,那么 f f f g g g 会在左半区间中产生交点, f f f 只有在左区间才可能优于 g g g,于是递归到左儿子中进行下传;
  • 反之,若在右端点处 f f f 更优,那么交点在右半区间中产生,递归到右儿子进行下传(图中的情况);
  • 如果左右端点 g g g 都更优,那么 f f f 不可能成为答案,不需要下传(即 g g g 整个在 f f f​​ 的上方)。

当交点正好就在 m i d mid mid 上时,我们直接将其归入 m i d mid mid f f f 不如 g g g 优的情况。这样我们就完成了对完全覆盖的区间的修改。对于其他部分覆盖的区间直接递归左右儿子解决即可。对于查询操作,将自己的答案与左右儿子取个 min ⁡ \min min 返回。

因为每次修改操作都需要递归左右儿子,所以加线段的复杂度是 O ( log ⁡ 2 n ) O(\log^2n) O(log2n) 的。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const int maxm = 40000;
struct Line {double k,b;
} L[maxn]; int cnt;
void addLine(int x0,int y0,int x1,int y1) {// 加入一条线段if (x0 == x1) L[++ cnt] = Line {0,max(y0,y1) * 1.0};else {double X = x1 - x0, Y = y1 - y0;L[++ cnt].k = Y / X, L[cnt].b = y0 - Y / X * x0;}
}
const double eps = 1e-9;
namespace LiChaoSegmentTree {#define lson l,mid,rt << 1#define rson mid + 1,r,rt << 1 | 1double K(int u,int d) {return L[u].b + L[u].k * d;}int check(double X,double Y) {return X - Y > eps ? 1 : Y - X > eps ? -1 : 0;}int ans[(maxm << 2) + 5];void color(int l,int r,int rt,int u) {int mid = l + r >> 1;int cM = check(K(u,mid), K(ans[rt],mid)); // 计算中点谁占优势if (cM == 1 || (cM == 0 && u < ans[rt])) // 总是令新线段不如旧线段优swap(u,ans[rt]); // 把自己更新好int cL = check(K(u,l),K(ans[rt],l));int cR = check(K(u,r),K(ans[rt],r));// 选择新线段可能更新的区域递归if (cL == 1 || (cL == 0 && u < ans[rt])) color(lson,u);if (cR == 1 || (cR == 0 && u < ans[rt])) color(rson,u);}int nowl,nowr;void modify(int l,int r,int rt,int u) {if (nowl <= l && r <= nowr)return color(l,r,rt,u);int mid = l + r >> 1;if (nowl <= mid) modify(lson,u);if (mid < nowr) modify(rson,u);}struct Answer { // 返回的答案int id; double val;bool operator<(const Answer &oth) const {int c = check(val,oth.val);return c == -1 ? 1 : c == 1 ? 0 : id > oth.id;}Answer(int X = 0,double Y = 0.0) { id = X, val = Y; }};int now;Answer query(int l,int r,int rt) { // 标记永久化,所以一路上需要不断地和自己的值取 maxif (l == r) return Answer{ans[rt],K(ans[rt],now)};int mid = l + r >> 1; Answer res(ans[rt],K(ans[rt],now));if (now <= mid) return max(query(lson),res);else return max(query(rson),res);}
} using namespace LiChaoSegmentTree;
int n,tmp;
const int P = 39989, Q = 1e9;
int chg(int X,int p) { return (X + tmp - 1 + p) % p + 1;
}
int main() {scanf("%d",&n);for (int i = 1,op,x0,x1,y0,y1,x;i <= n;i ++) {scanf("%d",&op);if (op == 1) {scanf("%d%d%d%d",&x0,&y0,&x1,&y1);x0 = chg(x0,P), x1 = chg(x1,P), y0 = chg(y0,Q), y1 = chg(y1,Q);if (x0 > x1) swap(x0,x1), swap(y0,y1);nowl = x0, nowr = x1;addLine(x0,y0,x1,y1);modify(1,maxm,1,cnt); } else {scanf("%d",&x), x = now = chg(x,P);printf("%d\n",tmp = query(1,maxm,1).id);}} return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/15868.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Photoshop插件(UXP)编写过程中,如何更新sp-checkbox的选中状态

✨问题说明 sp-checkbox是uxpSpectrum UXP Widgets下的一个小组件&#xff0c;内置样式大概是这样&#xff1a; 那么&#xff0c;如果用js动态的改变选中的状态&#xff0c;应该如何做呢&#xff1f; 如果直接是html来写&#xff1a; <sp-checkbox checked>Checked<…

特斯拉FSD的「端到端」到底能不能成?

引言 近年来&#xff0c;特斯拉的全自动驾驶&#xff08;Full Self-Driving&#xff0c;FSD&#xff09;技术备受关注&#xff0c;尤其是其「端到端」的AI软件框架更是引发了广泛讨论。端到端技术到底是一条正确的路径吗&#xff1f;它能否真正实现完全自动驾驶&#xff1f;本…

Echarts 实现将X轴放在图表顶部并且自动播放展示提示信息内容

文章目录 需求分析效果预览需求 如下图所示,实现柱状图中反转倒着绘制 分析 使用 ECharts 来实现对 Y 轴的倒序排序时,可以通过设置 yAxis 的 inverse 属性为 true 来实现。以下是一个简单的示例,演示了如何使用 ECharts 来创建一个柱状图,并将 Y 轴进行倒序排序:并且…

前缀和算法:提升编程效率的秘密武器(Java版)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能接…

代码审计--一道简单的文件包含题目的多种利用方式

NO.1 传统方法 首先来看下代码 <?php error_reporting(0); if(isset($_GET["file"])){include($_GET["file"]); }else{highlight_file(__FILE__);phpinfo(); } ?>看完代码后再来学习学习函数吧&#xff0c;毕竟菜啊&#xff01;&#xff01;&…

NASA数据集——阿尔法喷气式大气实验甲醛(HCHO)数据

Alpha Jet Atmospheric eXperiment Formaldehyde Data 简介 阿尔法喷气式大气实验甲醛数据 阿尔法喷气式大气实验&#xff08;AJAX&#xff09;是美国国家航空航天局艾姆斯研究中心与 H211, L.L.C. 公司的合作项目&#xff0c;旨在促进对加利福尼亚、内华达和太平洋沿岸地区的…

【NOIP2014普及组复赛】题4:子矩阵

题3&#xff1a;子矩阵 【题目描述】 给出如下定义&#xff1a; 1.子矩阵&#xff1a;从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵&#xff08;保持行与列的相对顺序&#xff09;被称为原矩阵的一个子矩阵。 例如&#xff0c;下面左图中选取第 2 、 4 2、4 2、…

vue项目中使用json编辑器

实现效果&#xff1a; 借助插件json-editor-vue3实现效果如图一&#xff0c;如果嫌丑可以通过类名改一下样式如图二。 实现过程&#xff1a; 安装插件&#xff1a;npm install json-editor-vue3 文档链接&#xff1a;GitCode - 开发者的代码家园 <script setup name&quo…

AcWing 3466. 清点代码库(STL:map,vector)

3466. 清点代码库 需要求有几种不同数列&#xff0c;每种有多少个&#xff0c;可以想到用map。它的键是一个数列&#xff0c;可以把它放在vector里。也就是map<vector<int>,int> 要满足要求的输出序列&#xff0c;就要想把它放在其他容器&#xff0c;或数组里&…

Vite + Vue3 部署 GitHub

因为静态资源是可以部署到 GitHub 上&#xff0c;自己顺便学习部署网站 因为我使用的是 Vite 工具&#xff0c;官方有提供相应 Demo 部署静态站点 | Vite 官方中文文档 新建文件夹 .github 然后再建一个文件夹 workflows 新建文件 main.yml 文件 直接使用官方文档 demo #…

如何处理时间序列的缺失数据

您是否应该删除、插入或估算&#xff1f; 世界上没有完美的数据集。每个数据科学家在数据探索过程中都会有这样的感觉&#xff1a; df.info()看到类似这样的内容&#xff1a; 大多数 ML 模型无法处理 NaN 或空值&#xff0c;因此如果您的特征或目标包含这些值&#xff0c;则在…

Java-MySql:JDBC

目录 JDBC概述 JDBC搭建 1、导入mysql开发商提供的jar包 2、注册驱动 3、与数据库连接 注解&#xff1a; Statement&#xff1a; 代码 运行 PreparedStatement&#xff1a; 代码 运行 PreparedStatement和Statement Statement 增 代码 运行 删 代码 运…

九、图形化脚本

多年来&#xff0c; shell脚本一直都被认为是枯燥乏味的。但如果你准备在图形化环境中运行脚本时&#xff0c;就未必如此了。有很多与脚本用户交互的方式并不依赖read和echo语句。 9.1 创建文本菜单 创建交互式shell脚本最常用的方法是使用菜单。提供各种选项可以帮助脚本用户…

AI遇上遥感,未来会怎样?

随着航空、航天、近地空间等多个遥感平台的不断发展&#xff0c;近年来遥感技术突飞猛进。由此&#xff0c;遥感数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量也大幅增长&#xff0c;使其越来越具有大数据特征。对于相关研究而言&#xff0c;遥感大数据的出现为其提…

基于MetaGPT构建LLM多智能体

前言 你好&#xff0c;我是GISer Liu&#xff0c;在上一篇文章中&#xff0c;我们用了两万多字详细拆解了单个Agent的组成&#xff0c;并通过Github Trending订阅智能体理解MetaGPT框架的订阅模块如何解决应用问题&#xff0c;但是对于复杂&#xff0c;并行的任务&#xff0c;单…

【vue】el-select选择器实现宽度自适应

选择器的宽度根据内容长度进行变化 <div class"Space_content"><el-selectv-model"value":placeholder"$t(bot.roommessage)"class"select"size"small"style"margin-right: 10px"change"selectcha…

JavaSE——集合框架二(1/6)-前置知识-可变参数、Collections工具类

目录 可变参数 Collections工具类 Collections的常用静态方法 实例演示 可变参数 可变参数 就是一种特殊形参&#xff0c;定义在方法、构造器的形参列表里&#xff0c;格式是&#xff1a;数据类型...参数名称 可变参数的特点和好处 特点&#xff1a;可以不传数据给它&am…

SQL常用基础语句(一)-- ABCDE开头

AS 将列名从 count(*) 修改为 total select count(*) as total from users where status0 将列名 username 改为 uname&#xff0c; password 改为 upwd select username as uname, password as upwd from users BETWEEN AND 说明&#xff1a;BETWEEN 筛选的是 >value1且 &l…

小程序主体变更是通过迁移吗?是需要2个小程序吗?

小程序迁移变更主体有什么作用&#xff1f;好多朋友都想做小程序迁移变更主体&#xff0c;但是又不太清楚具体有啥用&#xff0c;今天我就来详细说说。首先&#xff0c;小程序迁移变更主体最重要的作用就是可以修改主体。比如你的小程序原来是 A 公司的&#xff0c;现在 A 公司…

操作系统实验四:多线程与信号量编程

操作系统实验上机 更多技术请访问&#xff1a;www.xuanworld.top 部分审核不通过的文章将发至个人博客&#xff1a;www.xuanworld.top 欢迎来52破解论坛阅读帖子&#xff1a;https://www.52pojie.cn/thread-1891208-1-1.html 实验名称实验序号实验日期实验人多线程与信号量…