$ \color{#0066ff}{ 题目描述 }$

要求在平面直角坐标系下维护两个操作:

  1. 在平面上加入一条线段。记第 i 条被插入的线段的标号为 i
  2. 给定一个数 k,询问与直线 x = k 相交的线段中,交点最靠上的线段的编号。

有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i]。有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗车店,且会选择这些店中最便宜的一个进行一次消费。但是如果这个最便宜的价格大于c[i],那么这个人就不洗车了。请给每家店指定一个价格,使得所有人花的钱的总和最大。

\(\color{#0066ff}{输入格式}\)

第一行一个整数 n,表示共 n 个操作

接下来 n 行,每行第一个数为 0 或 1

若该数为 0,则后面跟着一个正整数 k,表示询问与直线 x = ((k + lastans – 1)%39989+1)相交的线段中交点(包括在端点相交的情形)最靠上的线段的编号,其中%表示取余。若某条线段为直线的一部分,则视作直线与线段交于该线段 y 坐标最大处。若有多条线段符合要求,输出编号最小的线段的编号

若该数为 1,则后面跟着四个正整数 x0, y0, x1, y1,表示插入一条两个端点为 ((x0+lastans-1)%39989+1,(y0+lastans-1)%\(10^9\)+1)和 ((x1+lastans-1)%39989+1,(y1+lastans-1)%\(10^9\)+1) 的线段

其中 lastans 为上一次询问的答案。初始时 lastans=0

\(\color{#0066ff}{输出格式}\)

对于每个 0 操作,输出一行,包含一个正整数,表示交点最靠上的线段的编 号。若不存在与直线相交的线段,答案为 0

\(\color{#0066ff}{输入样例}\)

6
1 8 5 10 8
1 6 7 2 6
0 2
0 9
1 4 7 6 7
0 5

\(\color{#0066ff}{输出样例}\)

2
0
3

\(\color{#0066ff}{数据范围与提示}\)

对于 30%的数据,n ≤ 1000

对于 100%的数据,1 ≤ n ≤ \(10^5\), 1 ≤ k, x0, x1 ≤ 39989, 1 ≤ y0 ≤ y1 ≤ \(10^9\)

\(\color{#0066ff}{题解}\)

考虑用线段树维护下标为x的最大的y

那么考虑两条直线

如果出现如图情况,显然BP和PD对答案其实就没有贡献了

线段树实际上维护的就是一个折线

但是对于一个区间,没办法记录一个直线,怎么办?

反正查询也是单点查询,我们可以考虑记录使得当前区间的mid位置的值最优的直线

然后看交点的位置,判断往哪个方向递归

实际上并不用求交点,更新当前区间的时候,如果现在的比当前区间的线段更优,就交换一下,用它去更新子树

这时候只要判断斜率就知道改往哪个方向递归了

这样操作其实是一种类似于标记永久化的东东,于是单点查询的时候要沿途取最优!

#include <bits/stdc++.h>
#define LL long long
#define mid ((l + r) >> 1)
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int maxn = 1e5 + 10;
const int mod1 = 39989;
const int mod2 = 1e9;
const double eps = 1e-6;
struct St {
protected:
struct node {
int s1, s2, id; double t1, t2; //线段两个端点,对应的y
node *ch[2];
node(int s1 = 0, int s2 = 0, int id = 0, double t1 = 0, double t2 = 0): s1(s1), s2(s2), id(id), t1(t1), t2(t2) {
ch[0] = ch[1] = NULL;
}
double gety(int x) { return s1 == s2? t1 : t1 + (getk() * (x - s1)); } //把x带入获得y
double getk() { return (double)(t2 - t1) / (double)(s2 - s1); } //斜率
double getmin() { return std::min(t1, t2); } //区间最小y
double getmax() { return std::max(t1, t2); } //区间最大y
void ltrn(int x) { t1 = gety(x); s1 = x; } //有一个x = (this)x的直线截取当前线段,取右边
void rtrn(int x) { t2 = gety(x); s2 = x; } //同上,取左边,就是缩小线段到当前区间范围内
}*root, pool[maxn], *tail;
int n;
bool check(node *a, node *b, int x) { //看看把x带入a是否比b优
double vala = a->gety(x), valb = b->gety(x);
return fabs(vala - valb) <= eps? a->id < b->id : vala > valb;
}
void build(node *&o, int l, int r) {
o = new node(l, r);
if(l == r) return;
build(o->ch[0], l, mid);
build(o->ch[1], mid + 1, r);
}
node *query(node *o, int l, int r, int pos) {
if(l == r) return o;
node *ans;
if(pos <= mid) ans = query(o->ch[0], l, mid, pos);
else ans = query(o->ch[1], mid + 1, r, pos);
return check(ans, o, pos)? ans : o; //每次取最优
}
void lazy(node *o, int l, int r, node *k) {
if(o->s1 > k->s1) k->ltrn(o->s1); //缩小线段的x范围到当前区间
if(o->s2 < k->s2) k->rtrn(o->s2);
if(check(k, o, mid)) {
std::swap(o->s1, k->s1); //更优则交换
std::swap(o->s2, k->s2);
std::swap(o->t1, k->t1);
std::swap(o->t2, k->t2);
std::swap(o->id, k->id);
}
if(o->getmin() >= k->getmax()) return; //没有贡献
if(o->getk() <= k->getk()) lazy(o->ch[1], mid + 1, r, k);
else lazy(o->ch[0], l, mid, k);
}
void ins(node *o, int l, int r, node *k) {
if(k->s1 > r || k->s2 < l) return;
node *newk = new(tail++) node(); *newk = *k; //这里必须开一个新的点,要保证递归完k并没有改变
if(o->s1 > newk->s1) newk->ltrn(o->s1);
if(o->s2 < newk->s2) newk->rtrn(o->s2);
if(l == newk->s1 && r == newk->s2) return lazy(o, l, r, newk);
ins(o->ch[0], l, mid, newk);
ins(o->ch[1], mid + 1, r, newk);
}
public:
void init(int n) { this->n = n; root = NULL; build(root, 1, n); }
void ins(int s1, int s2, int id, int t1, int t2) {
tail = pool;
node *o = new node(s1, s2, id, t1, t2);
if(s1 == s2) o->t1 = o->t2 = o->getmax();
ins(root, 1, n, o);
}
int query(int pos) { return query(root, 1, n, pos)->id; }
}T;
int main(){
T.init(mod1);
int ans = 0, cnt = 0, s1, s2, t1, t2;
for(int Q = in(); Q --> 0;) {
if (!in()) printf("%d\n", ans = T.query((in() + ans - 1) % mod1 + 1));
else{
s1 = (in() + ans - 1) % mod1 + 1;
t1 = (in() + ans - 1) % mod2 + 1;
s2 = (in() + ans - 1) % mod1 + 1;
t2 = (in() + ans - 1) % mod2 + 1;
if(s1 > s2) std::swap(s1, s2), std::swap(t1, t2);
T.ins(s1, s2, ++cnt, t1, t2);
}
}
}

P4097 [HEOI2013]Segment 李超线段树的更多相关文章

  1. Luogu P4097 [HEOI2013]Segment 李超线段树

    题目链接 \(Click\) \(Here\) 李超线段树的模板.但是因为我实在太\(Naive\)了,想象不到实现方法. 看代码就能懂的东西,放在这里用于复习. #include <bits/ ...

  2. 【洛谷P4097】Segment 李超线段树

    题目大意:维护一个二维平面,给定若干条线段,支持询问任意整数横坐标处对应的纵坐标最靠上的线段的 id,相同高度取 id 值较小的,强制在线. 题解:初步学习了李超线段树.李超线段树的核心思想在于通过标 ...

  3. BZOJ3165: [Heoi2013]Segment(李超线段树)

    题意 题目链接 Sol 李超线段树板子题.具体原理就不讲了. 一开始自己yy着写差点写自闭都快把叉积搬出来了... 后来看了下litble的写法才发现原来可以写的这么清晰简洁Orz #include& ...

  4. 【BZOJ 3165】 [Heoi2013]Segment 李超线段树

    所谓李超线段树就是解决此题一类的问题(线段覆盖查询点最大(小)),把原本计算几何的题目变成了简单的线段树,巧妙地结合了线段树的标记永久化与标记下传,在不考虑精度误差的影响下,打法应该是这样的. #in ...

  5. BZOJ3165[Heoi2013]Segment——李超线段树

    题目描述 要求在平面直角坐标系下维护两个操作: 1.在平面上加入一条线段.记第i条被插入的线段的标号为i. 2.给定一个数k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号. 输入 第一行 ...

  6. BZOJ.3165.[HEOI2013]Segment(李超线段树)

    BZOJ 洛谷 对于线段,依旧是存斜率即可. 表示精度误差一点都不需要管啊/托腮 就我一个人看成了mod(10^9+1)吗.. //4248kb 892ms #include <cstdio&g ...

  7. 【BZOJ-3165】Segment 李超线段树(标记永久化)

    3165: [Heoi2013]Segment Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 368  Solved: 148[Submit][Sta ...

  8. 2019.02.11 bzoj3165: [Heoi2013]Segment(线段树)

    传送门 题意简述:要求支持两种操作: 插入一条线段. 询问与直线x=kx=kx=k相交的线段中,交点最靠上的线段的编号. 思路: 直接上李超线段树即可. 代码: #include<bits/st ...

  9. Segment 李超线段树

    题目大意: 要求在平面直角坐标系下维护两个操作: 1.在平面上加入一条线段.记第 i 条被插入的线段的标号为 i 2.给定一个数 k,询问与直线 x = k 相交的线段中,交点最靠上的线段的编号. 若 ...

随机推荐

  1. MemoryUsage:监测java虚拟机内存使用

    通过MemoryUsage可以查看Java 虚拟机的内存池的内存使用情况.MemoryUsage类有四个值(均以字节为单位): ===Init=== java虚拟机在启动的时候向操作系统请求的初始内存 ...

  2. mybatis思维导图(二)

    写在前面 上一篇文章写了mybatis的基本原理和配置文件的基本使用,这一篇写mybatis的使用,主要包括与sping集成.动态sql.还有mapper的xml文件一下复杂配置等.值得注意的是,导图 ...

  3. 前端福利之改变placeholder颜色的方法(转)

    之前拿到一个设计图,Placeholder是白色的,所以就查看了一下改变placeholder的方法: input::-webkit-input-placeholder { /* WebKit bro ...

  4. unity 确定敌人行走路线

    一开始搞这个问题很头疼,无从下手. 1.敌人在随机地点产生后,每个敌人有要有自己自动的行走路线,目的地是保护地,而且行走路线要多样化. 2.敌人在看到玩家时,改变行走路线,向玩家的方向行进,且到了一定 ...

  5. 深入理解java虚拟机(十二) Java 语法糖背后的真相

    语法糖(Syntactic Sugar),也叫糖衣语法,是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语.指的是,在计算机语言中添加某种语法,这些语法糖虽然不会对语言 ...

  6. Javascript与数据结构系列(二)——队列的实现

    队列实现 使用数组来实现队列看起来顺理成章.JavaScript 中的数组具有其他编程语言中没有的优点, 数组的 push() 方法可以在数组末尾加入元素,shift() 方法则可删除数组的第一个元素 ...

  7. 如何在github上上传readme文件

    首先打开记事本写好文字 然后保存为readme.md文件 打开github网页,登录自己的账号,选择右上角new repository 填写信息,勾选选项如下 创建好之后,选择upload files ...

  8. C# 调取摄像头 +拍照

    1.添加引用 using System.Windows.Media.Imaging; using AForge; using AForge.Controls; using AForge.Video; ...

  9. atan2()如何转换为角度

    atan2()如何转换为角度 Math.atan2()函数返回点(x,y)和原点(0,0)之间直线的倾斜角.那么如何计算任意两点间直线的倾斜角呢?只需要将两点x,y坐标分别相减得到一个新的点(x2-x ...

  10. Consul ACL

    consul自带ACL控制功能,看了很多遍官方文档,没有配置步骤https://www.consul.io/docs/internals/acl.html 主要对各种配置参数解释,没有明确的步骤,当时 ...