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 相交的线段中,交点最靠上的线段的编号. 若 ...
随机推荐
- 169. Majority Element 出现次数超过n/2的元素
[抄题]: Given an array of size n, find the majority element. The majority element is the element that ...
- RTX Server SDK跨服务器
反向单点登录RTX,即通过腾讯通登录到其他的系统,登录腾讯通后其他的系统不用输入用户名和密码即可登录.可能有二种情况.腾讯通和其他系统在同一台服务器上或在不同的服务器上.要调用腾讯通的登录验证接口的系 ...
- TCP、UDP、HTTP、SOCKET之间的区别与联系-乾颐堂CCIE
IP:网络层协议: TCP和UDP:传输层协议: HTTP:应用层协议: SOCKET:TCP/IP网络的API. TCP/IP代表传输控制协议/网际协议,指的是一系列协议. TCP和UDP使用IP协 ...
- tp5写日志
- TFS(Visual Studio Team Services) git认证失败 authentication fails 的解决方案
问题描述 TFS 在visual studio中使用正常,可是git pull运行失败,提示 authentication fails. 初步判断原因为默认的 credential.helper 与 ...
- ofo开锁共享平台
http://www.cnblogs.com/mengyu/p/7700980.html
- C#中把任意类型的泛型集合转换成SQLXML数据格式的小例子
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.D ...
- mongodb ---- findAndModify 写法
db.coll.findAndModify({ query:{x:"ggg"}, update:{$set:{"x":"gggg"}}, f ...
- xampp 添加ssl 访问
编辑php.ini 文件,找到 “;extension=php_openssl.dll” (去掉前面的;号注释) <VirtualHost *:8090> DocumentRoot &qu ...
- Typora的图片根目录设置,
需求:使Typora的图片,设置到指定的文件里. 方便上传与转移. 步骤: 1 位置: 编辑 ->图片工具->设置图片根目录. 2 .Preference -> Editor -&g ...