P4097 [HEOI2013]Segment 李超线段树
$ \color{#0066ff}{ 题目描述 }$
要求在平面直角坐标系下维护两个操作:
- 在平面上加入一条线段。记第 i 条被插入的线段的标号为 i
- 给定一个数 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 李超线段树的更多相关文章
- Luogu P4097 [HEOI2013]Segment 李超线段树
题目链接 \(Click\) \(Here\) 李超线段树的模板.但是因为我实在太\(Naive\)了,想象不到实现方法. 看代码就能懂的东西,放在这里用于复习. #include <bits/ ...
- 【洛谷P4097】Segment 李超线段树
题目大意:维护一个二维平面,给定若干条线段,支持询问任意整数横坐标处对应的纵坐标最靠上的线段的 id,相同高度取 id 值较小的,强制在线. 题解:初步学习了李超线段树.李超线段树的核心思想在于通过标 ...
- BZOJ3165: [Heoi2013]Segment(李超线段树)
题意 题目链接 Sol 李超线段树板子题.具体原理就不讲了. 一开始自己yy着写差点写自闭都快把叉积搬出来了... 后来看了下litble的写法才发现原来可以写的这么清晰简洁Orz #include& ...
- 【BZOJ 3165】 [Heoi2013]Segment 李超线段树
所谓李超线段树就是解决此题一类的问题(线段覆盖查询点最大(小)),把原本计算几何的题目变成了简单的线段树,巧妙地结合了线段树的标记永久化与标记下传,在不考虑精度误差的影响下,打法应该是这样的. #in ...
- BZOJ3165[Heoi2013]Segment——李超线段树
题目描述 要求在平面直角坐标系下维护两个操作: 1.在平面上加入一条线段.记第i条被插入的线段的标号为i. 2.给定一个数k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号. 输入 第一行 ...
- BZOJ.3165.[HEOI2013]Segment(李超线段树)
BZOJ 洛谷 对于线段,依旧是存斜率即可. 表示精度误差一点都不需要管啊/托腮 就我一个人看成了mod(10^9+1)吗.. //4248kb 892ms #include <cstdio&g ...
- 【BZOJ-3165】Segment 李超线段树(标记永久化)
3165: [Heoi2013]Segment Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 368 Solved: 148[Submit][Sta ...
- 2019.02.11 bzoj3165: [Heoi2013]Segment(线段树)
传送门 题意简述:要求支持两种操作: 插入一条线段. 询问与直线x=kx=kx=k相交的线段中,交点最靠上的线段的编号. 思路: 直接上李超线段树即可. 代码: #include<bits/st ...
- Segment 李超线段树
题目大意: 要求在平面直角坐标系下维护两个操作: 1.在平面上加入一条线段.记第 i 条被插入的线段的标号为 i 2.给定一个数 k,询问与直线 x = k 相交的线段中,交点最靠上的线段的编号. 若 ...
随机推荐
- 怎样用jQuery拿到select中被选中的option的值
1.首先要保证select中每一个option标签都有value属性: 2.jquery的写法 1 $('#sele').val()//这里假设select的id是sele,这样可以获取当前选中的op ...
- jvisualvm远程监控服务器tomcat
1.在 {服务器tomcat路径}/bin/catalina.sh 中,的[# OS specific support. $var _must_ be set to either true or f ...
- [Training Video - 6] [File Reading] [Java] Read Excel File Using Apache POI API
读取以下两种格式的Excel : *.xls and *.xlsx 用Apache POI API来实现,需要用到 HSSF 和 XSSF 的类库 HSSF is the POI Project's ...
- mysql存储过程和触发器
mysql编程(存储过程和触发器) 存储过程 什么是存储过程 存储过程,带有逻辑的sql语句 存储过程特点 执行效率非常快!存储过程是在数据库的服务器端执行的!!! 移植性很差!不同数据库的存储过程是 ...
- twitter集成第三方登录是窗口一直出现闪退的解决方法
需要创建自己的token,如下图
- mysql user表 权限字段详解
Select_priv.确定用户是否可以通过SELECT命令选择数据. Insert_priv.确定用户是否可以通过INSERT命令插入数据. Update_priv.确定用户是否可以通过UPDATE ...
- Boost学习之语法解析器--Spirit
Boost.Spirit能使我们轻松地编写出一个简单脚本的语法解析器,它巧妙利用了元编程并重载了大量的C++操作符使得我们能够在C++里直接使用类似EBNF的语法构造出一个完整的语法解析器(同时也把C ...
- 20169202 2016-2017-2《TCP/IP协议攻击》实验总结--十一周
APR缓存中毒(ARP cache poisoning) 实验原理 ARP缓存是ARP协议的重要组成部分.ARP协议运行的目标就是建立MAC地址和IP地址的映射,然后把这一映射关系保存在ARP缓存中, ...
- 【Android开发精要笔记】Android组件模型解析
Android组件模型解析 Android中的Mashup 将应用切分成不同类别的组件,通过统一的定位模型和接口标准将他们整合在一起,来共同完成某项任务.在Android的Mashup模式下,每个组件 ...
- C#中的异步调用及异步设计模式(三)——基于事件的异步模式
四.基于事件的异步模式(设计层面) 基于事件的C#异步编程模式是比IAsyncResult模式更高级的一种异步编程模式,也被用在更多的场合.该异步模式具有以下优点: · ...