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

news2024/9/23 9:32:12

题意

有一个平面直角坐标系,总共 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 | 1
    double 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) { // 标记永久化,所以一路上需要不断地和自己的值取 max
        if (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.coloradmin.cn/o/1696100.html

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

相关文章

【面试干货】完全平方数

【面试干货】完全平方数 1、实现思想2、代码实现 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 一个整数&#xff0c;它加上 100 后是一个完全平方数&#xff0c;再加上 168 又是一个完全平方数&#xff0c;请问该数是多少&#xff1f; 1、…

设计模式 17 组合模式 Composite Pattern

设计模式 17 组合模式 Composite Pattern 1.定义 组合模式&#xff08;Composite Pattern&#xff09;&#xff0c;又叫部分整体模式&#xff0c;是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象&#xff0c;用来表示部分以及整体层次。这种类型的设…

wps使用(解决毕业论文)

目录自动生成 页码自动生成 一部分使用I II III IV 格式&#xff0c;一部分使用1&#xff0c;2&#xff0c;3&#xff0c;4 格式 先设置全部文章为I II III IV 格式&#xff0c;然后再需要的地方再设置1&#xff0c;2&#xff0c;3&#xff0c;4 格式 一键设置中文、英文、数…

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

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

Java面试八股之什么是锁消除和锁粗化

什么是锁消除和锁粗化 锁消除&#xff08;Lock Elimination&#xff09;&#xff1a; 锁消除是Java虚拟机&#xff08;JVM&#xff09;进行的一种高级优化策略&#xff0c;旨在消除那些没有必要存在的同步操作&#xff0c;以减少不必要的性能开销。这一优化发生在即时编译器&a…

Docker拉取镜像报错:x509: certificate has expired or is not yet v..

太久没有使用docker进行镜像拉取&#xff0c;今天使用docker-compose拉取mongo发现报错&#xff08;如下图&#xff09;&#xff1a; 报错信息翻译&#xff1a;证书已过期或尚未有效。 解决办法&#xff1a; 1.一般都是证书问题或者系统时间问题导致&#xff0c;可以先执行 da…

Nginx-狂神说

Nginx概述 公司产品出现瓶颈&#xff1f; 我们公司项目刚刚上线的时候&#xff0c;并发量小&#xff0c;用户使用的少&#xff0c;所以在低并发的情况下&#xff0c;一个jar包启动应用就够了&#xff0c;然后内部tomcat返回内容给用户。 但是慢慢的&#xff0c;使用我们平台…

微信小程序- 实现横向滑动列表

1. 微信小程序-实现横向滑动列表 微信小程序如何隐藏scroll-view滚动条    1.1. photoScroll.wxml <view class"hs-body"><scroll-view class"hs-layout" scroll-x"true" scroll-left"{{x}}" scroll-with-animation&quo…

设计循环队列(C语言)怎会如此简单!!!

目录 题目题目分析 解答结构体初始化判空判满插入删除去队头数据取队尾数据队列的销毁 题目 链接: 题目 设计你的循环队列实现。 循环队列是一种线性数据结构&#xff0c;其操作表现基于 FIFO&#xff08;先进先出&#xff09;原则并且队尾被连接在队首之后以形成一个循环。它…

AI Agent: Agent框架+7个实例

何谓Agent Agent 作为一种新兴的人工智能技术&#xff0c;正在受到越来越多的关注。要说清楚什么是 Agent&#xff0c;先得看看人工智能的本质是什么。 人工智能这个名称来自它试图通过计算机程序或机器来模拟、扩展和增强人类智能的 一些方面。在这个定义中&#xff0c;“人…

【QGIS入门实战精品教程】10.6:QGIS制作酒店分布热力图

相关阅读: ArcGIS实验教程——实验四十二:ArcGIS密度分析(核密度、点密度、线密度) 【ArcGIS微课1000例】0086:基于七普人口数据的人口密度分析与制图 ArcGIS实验教程——实验二十四:人口密度制图 文章目录 一、加载酒店分布数据二、热力分析一、加载酒店分布数据 订阅专…

LeetCode刷题之HOT100之合并二叉树

2024/5/26 晴。是的&#xff0c;等下我要去长乐沙滩赶海哈哈&#xff0c;因为这几天数字峰会&#xff0c;地铁公交又免费啦。ok&#xff0c;今天做的是HOT100里面最后一道easy题目啦&#xff0c;明天就是要跨越一个难度啦&#xff01;做题吧 1、题目描述 2、逻辑分析 题目要求…

Python脚本必加代码:99%的程序员都忽视了这个细节!

文章目录 一、初识 if __name__ __main__二、__name__ 和 __main__ 是什么&#xff1f;三、实战讲解四、实际应用场景测试代码提高代码可重用性避免不必要的执行 五、深入理解和更多用法使用 argparse 解析命令行参数使用 unittest 进行单元测试使用 multiprocessing 创建子进…

任推邦:实力强劲的APP推广拉新平台,号称不扣量

任推邦简介 任推邦是国内数一数二的项目分发平台&#xff0c;也是一个不扣量的项目APP推广拉新平台&#xff0c;隶属于聚名科技集团股份有限公司。聚名科技成立时间在2012年&#xff0c;是安徽省老牌互联网企业&#xff0c;历经11年的飞速发展&#xff0c;聚名科技成功布局打造…

Adobe Bridge BR v14.0.3 安装教程 (多媒体文件组织管理工具)

Adobe系列软件安装目录 一、Adobe Photoshop PS 25.6.0 安装教程 (最流行的图像设计软件) 二、Adobe Media Encoder ME v24.3.0 安装教程 (视频和音频编码渲染工具) 三、Adobe Premiere Pro v24.3.0 安装教程 (领先的视频编辑软件) 四、Adobe After Effects AE v24.3.0 安装…

探秘NumPy的奥秘:元素级操作与广播机制

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、NumPy基础与元素级操作 元素级操作的引入 元素级操作详解 广播机制初探 二、NumPy矩…

PostgreSQL基础(三):PostgreSQL的基础操作

文章目录 PostgreSQL的基础操作 一、用户操作 二、权限操作 三、操作任务

全免费的数据恢复工具哪个好?分享2024年性价比超高的12款数据恢复软件!

当您丢失重要文件时&#xff0c;您应该可不想遇到措手不及的情况吧&#xff1f;相反&#xff0c;您需要在系统中使用一些可靠的数据恢复软件&#xff0c;但是全免费的数据恢复工具哪个好呢&#xff1f;别担心&#xff0c;本文将帮助您选择最适合您的解决方案。 如何挑选一款合适…

Linux 基本使用和 web 程序部署云端

目录 1.Linux发行版 2.Linux常用命令 ls pwd cd touch mkdir cat rm cp mv man vim grep ps netstat 绝对路径 vs 相对路径 使用 tab 键补全 使用 ctrl c 重新输入 粘贴与复制快捷键 3.Linux环境搭建 环境搭建方式 使用云服务器 4.搭建Java部署环境 …

CnosDB:深入理解时序数据质量函数

在CnosDB中&#xff0c;我们设计并实现了计算数据质量的多个指标&#xff0c;这些指标可以从多个维度评估时序数据的质量&#xff0c;对于时间戳列&#xff0c;我们考虑数据的缺失点、冗余点和延迟点。对于值列&#xff0c;我们考虑数据的异常值、范围、变化、速度和加速度。 C…