「雅礼集训 2017 Day2」线段游戏(线段树懒标记“启发式下传”,李超树)
题面

题解
加入一条线段,可以把它转化为在[L,R]区间内加一条线 y=ax+b (如果原线段与y轴平行,就相当于在{x1}处加一条线 y=max(y1,y2))
我们可以把它加到线段树上,线段树上每个点存一个区间内贯穿整个区间的一条对答案有贡献的线段(因为是贯穿整个区间,所以存一个 {a,b} 表示 y=ax+b 就行了)。
一条线段是这么加上去的:

查询要遍历路径上的所有点:

接下来,最大的问题就是,它会产生冲突!
当我们加一条线段到线段树上,而原来节点上就已经有了一条线段怎么办?
首先,如果其中一条线段凌驾于另一条之上,肯定直接判了嘛,
但是,出问题的就是两条线段交叉,

而线段树上的节点存不了“V”字形,也维护不了(除了写平衡树的大佬)
这里就要用到一个“启发式下传”的思想
一般的暴力想法就是先把原来的线段先下传了,再把新线段放上去:

稍微有点想法的人会先把“V”字形处理出来,并把两条线段拆开下传(虽然并没有什么*用):

这时候,较聪明的人就会想,为什么原来的地方要空着呢?把其中随便一条线段留在原处,对答案也没有影响,但是却快得多:

最后,深谙暴力优化精髓的人会想,数据肯定会卡每次下传的线段长度只比原先段短一点的情况,便把“V”字形两条线段中短的一条下传,
于是便优化出了正解:

我们会发现,每次一旦冲突,便会开始下传,每次下传的线段长度至少会减半,所以,下传复杂度为O(log),加线段复杂度就为O(log^2)
查询复杂度没变,总复杂度就为O(mlog^2)
CODE
#include<map>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 50005
#define MAXM 100005
#define LL long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) ((-x)&(x))
//#define int LL
//#pragma GCC optimize(2)
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s=getchar();}
return f*x;
}
const int MOD = 998244353;
int n,m,i,j,s,o,k;
struct it{
DB x,y;
it(){x = y = 0;}
it(DB X,DB Y){x=X;y=Y;}
};
it cg(it a,it b) {
DB k = (a.y - b.y) / (a.x - b.x);
DB B = a.y - a.x * k;
return it(k,B);
}
it jd(it a,it b) {
DB xx = (b.y - a.y) / (a.x - b.x);
return it(xx,a.x*xx+a.y);
}
bool abov(it a,it st) {
DB y2 = a.x * st.x + st.y;
return a.y >= y2;
}
bool operator < (it a,it b) {return a.y < b.y;}
bool operator > (it a,it b) {return b < a;}
bool operator <= (it a,it b) {return a.y <= b.y;}
bool operator >= (it a,it b) {return b <= a;}
struct tr{
int l,r;
it st;
tr(){l=r=0;st=it(0,-1e9);}
}tre[MAXM<<4];
void maketree(int a,int l,int r) {
tre[a].l = l;tre[a].r = r;
if(l < r) {
int mid = (l+r)>>1;
maketree(a<<1,l,mid);
maketree(a<<1|1,mid+1,r);
}
return ;
}
void addtree(int a,int l,int r,it st) {
if(tre[a].l > r || tre[a].r < l) return ;
if(tre[a].l >= l && tre[a].r <= r) {
DB ll = (DB)tre[a].l,rr = (DB)tre[a].r;
it l1 = it(ll,ll*tre[a].st.x+tre[a].st.y);
it r1 = it(rr,rr*tre[a].st.x+tre[a].st.y);
it l2 = it(ll,ll*st.x+st.y),r2 = it(rr,rr*st.x+st.y);
if(l1 >= l2 && r1 >= r2) return ;
if(l2 >= l1 && r2 >= r1) {
tre[a].st = st; return ;
}
it md = jd(st,tre[a].st); int mid = (tre[a].l+tre[a].r)>>1;
it ls = (l1 >= l2 ? tre[a].st:st),rs = (r1 >= r2 ? tre[a].st:st);
if(md.x <= (DB)mid) tre[a].st = rs,addtree(a<<1,l,r,ls);
else tre[a].st = ls,addtree(a<<1|1,l,r,rs);
return ;
}
addtree(a<<1,l,r,st);addtree(a<<1|1,l,r,st);
return ;
}
DB query(int a,int ad) {
if(tre[a].l > ad || tre[a].r < ad) return -1e9;
if(tre[a].l == tre[a].r) {
return ad*tre[a].st.x + tre[a].st.y;
}
return max(ad*tre[a].st.x+tre[a].st.y,max(query(a<<1,ad),query(a<<1|1,ad)));
}
int main() {
n = read();m = read();
maketree(1,1,100000);
for(int i = 1;i <= n;i ++) {
it a,b;
a.x = (DB)read();
a.y = (DB)read();
b.x = (DB)read();
b.y = (DB)read();
if(a.x > b.x) swap(a,b);
it st;
if(a.x == b.x) st = it(0,max(a.y,b.y));
else st = cg(a,b);
int ll = (int)a.x,rr = (int)b.x;
addtree(1,ll,rr,st);
}
for(int i = 1;i <= m;i ++) {
k = read();
if(!k) {
it a,b;
a.x = (DB)read();
a.y = (DB)read();
b.x = (DB)read();
b.y = (DB)read();
if(a.x > b.x) swap(a,b);
it st;
if(a.x == b.x) st = it(0,max(a.y,b.y));
else st = cg(a,b);
int ll = (int)a.x,rr = (int)b.x;
addtree(1,ll,rr,st);
}
else {
int x0 = read();
DB ans = query(1,x0);
printf("%.4f\n",ans < -1e8 ? 0.0:ans);
}
}
return 0;
}
「雅礼集训 2017 Day2」线段游戏(线段树懒标记“启发式下传”,李超树)的更多相关文章
- #6034. 「雅礼集训 2017 Day2」线段游戏 李超树
#6034. 「雅礼集训 2017 Day2」线段游戏 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统 ...
- 【loj6034】「雅礼集训 2017 Day2」线段游戏
#6034. 「雅礼集训 2017 Day2」线段游戏 内存限制:256 MiB 时间限制:1000 ms 标准输入输出 题目类型:传统 评测方式:Special Judge 上传者: 匿名 题目描述 ...
- 「雅礼集训 2017 Day2」解题报告
「雅礼集训 2017 Day2」水箱 我怎么知道这种题目都能构造树形结构. 根据高度构造一棵树,在树上倍增找到最大的小于约束条件高度的隔板,开一个 \(vector\) 记录一下,然后对于每个 \(v ...
- 【LOJ6029】「雅礼集训 2017 Day1」市场(线段树裸题)
点此看题面 大致题意: 维护序列,支持区间加法,区间除法(向下取整),区间求\(min\)和区间求和. 线段树维护区间除法 区间加法.区间求\(min\)和区间求和都是线段树基本操作,因此略过不提. ...
- loj #6032. 「雅礼集训 2017 Day2」水箱 线段树优化DP转移
$ \color{#0066ff}{ 题目描述 }$ 给出一个长度为 \(n\) 宽度为 \(1\) ,高度无限的水箱,有 \(n-1\) 个挡板将其分为 \(n\) 个 \(1 - 1\) 的小格, ...
- loj#6034 「雅礼集训 2017 Day2」线段游戏
分析 区间李超树板子题 代码 #include<bits/stdc++.h> using namespace std; #define db double const int inf = ...
- LOJ#6032. 「雅礼集训 2017 Day2」水箱
传送门 首先可以有一个平方复杂度的 \(DP\) 设 \(f_{i,j}\) 表示前面 \(i\) 个小格,高度为 \(j\) 的最大答案 令 \(h_i\) 表示隔板 \(i\) 的高度 当 \(j ...
- LOJ#6048. 「雅礼集训 2017 Day10」数列(线段树)
题面 传送门 题解 我的做法似乎非常复杂啊-- 首先最长上升子序列长度就等于把它反过来再接到前面求一遍,比方说把\(2134\)变成\(43122134\),实际上变化之后的求一个最长上升子序列和方案 ...
- 「雅礼集训 2017 Day2」水箱
题目链接 题意分析 我们用\(f[i][j]\)表示当前到达第\(i\)个位置水位高度为\(j\)的答案 如果那么\(h[i]\)为\(i\)和\(i+1\)之间的支柱高度 那么如果\(j≤h[i]\ ...
随机推荐
- 动态线程池框架 DynamicTp v1.0.6版本发布。还在为Dubbo线程池耗尽烦恼吗?还在为Mq消费积压烦恼吗?
DynamicTp 简介 DynamicTp 是一个基于配置中心实现的轻量级动态线程池管理工具,主要功能可以总结为 动态调参.通知报警.运行监控.三方包线程池管理等几大类. 经过几个版本迭代,目前最新 ...
- BUUCTF-LSB
LSB 看到这个题目应该是LSB隐写,StegSolve打开,在红绿蓝0号上发现图片信息 然后在Analyse选择data extract Save bin保存图片即可 得到的是个二维码,解码即可.
- Windows-matlab简易安装-用于数字图像处理
安装 下载链接 解压文件得到 双击setup.exe 主要注意几点 使用文件安装密匙 只需安装这三个即可 将两个文件夹里面的dll文件复制到安装目录的 /bin/win64 目录 两个 .lic 文件 ...
- UiPath存在文本Text Exists的介绍和使用
一.Text Exists的介绍 检查是否在给定的UI元素中找到了文本,输出的是一个布尔值 二.Text Exists在UiPath中的使用 1. 打开设计器,在设计库中新建一个Sequence,为序 ...
- CSS 盒子模型(一)
CSS 盒子模型(一) 本人在校学生,主学后端,后来发现前端的基础都忘得差不多了才想着写文章回来复习!欢迎留言交流. 什么是盒子呢? 拿下举例,我们可以把每个红框都比作一个盒子,他们可以是任意的 HT ...
- day11 Java反射机制
java反射机制 反射是java中的动态机制,它允许我们在程序运行期间再确定类的实例化,方法的调用,属性的调用等,而不是传统意义上的在编码期间确定. 因此,反射可以大大的提高代码的灵活度,但是随之而来 ...
- Note -「模拟退火」
随机化算法属于省选芝士体系 0x01 前置芝士 你只需要会 rand 就可以啦! 当然如果你想理解的更透彻也可以先看看 爬山算法 0x02 关于退火 退火是一种金属热处理工艺,指的是将金属缓慢加热到一 ...
- 如何创建一个带诊断工具的.NET镜像
现阶段的问题 现在是云原生和容器化时代,.NET Core对于云原生来说有非常好的兼容和亲和性,dotnet社区以及微软为.NET Core提供了非常方便的镜像容器化方案.所以现在大多数的dotnet ...
- PLC转OPC UA的协议转换网关需要多少钱呢?
嵌入式OPC UA网关BL102简化了OPC UA程序的开发与IIOT工业物联网应用 在制造业数字化升级过程中,我们碰到最多的工作便是针对每一款PLC去开发一套OPC UA程序,然后通过这套程序去读取 ...
- 基于mpvue的框架开发微信小程序(搭建环境)
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_103 美团很早就开源了mpvue这个项目,如此看来,美团可不仅仅是一家团购网站,真正的技术驱动型企业,使得我们多了一种用来开发微信 ...