懒人的福利?教你用set维护斜率优化凸包
斜率优化题目大家肯定都做得不少了,有一些题目查询插入点的x坐标和查询斜率都不单调,这样就需要维护动态凸包并二分斜率。(例如bzoj1492)
常规的做法是cdq分治或手写平衡树维护凸包,然而如果我不愿意写分治,也懒得打平衡树,怎么办呢?
没关系,今天我告诉你怎么用一个set维护这种凸包。
首先orzLH,没什么特殊意义,只是单纯的orz。
我们定义f[i]表示在第i天能拥有的金券组数,按照第i天的比例。
那么,我们要把前面的金券在今天卖出获得最多的钱,并在今天进行买入。
所以,f[i]=max((f[j]*a[i]+f[j]/rate[j]*b[i])/(a[i]+rate[i]*b[i]))。
除下去的东西是一个常数,扔掉。
然后我们就有:
t=max(a[i]*(f[j])+b[i]*(f[j]/rate[j]))。
如果我们把f[j]看做x,f[j]/rate[j]看做y,我们有:
t=a*x+b*y,两边同时除以b,得到:
t/b=(a/b)x+y
y=(t/b)-(a/b)*x
好的,现在我们有一条斜率为-(a/b)的直线,要找一个点使之截距最大。
这样我们维护一个右上1/4凸壳即可。
怎么维护?
我们考虑不用斜率优化,单纯水平序维护凸包,那么点是按照x坐标单增在平衡树上排列的。
现在我们在每个点维护他与后面点连线斜率,我们会发现:这个斜率是单降的。
所以,我们可以通过适当地转换cmp函数,来通过一个set完成两种比较。
我们定义:
int cmp; // 0 compare x , 1 compare slope .
struct Point {
double x,y,slop;
friend bool operator < (const Point &a,const Point &b) {
if( !cmp ) return a.x != b.x ? a.x < b.x : a.y < b.y;
return a.slop > b.slop;
}
friend Point operator - (const Point &a,const Point &b) {
return (Point){a.x-b.x,a.y-b.y};
}
friend double operator * (const Point &a,const Point &b) {
return a.x * b.y - b.x * a.y;
}
inline double calc(double a,double b) const {
return a * x + b * y;
}
};
set<Point> st;
插入就是正常凸包插入,最后再维护一下斜率就行了。注意弹出左边后迭代器会失效,所以需要重新lower_bound一下。(可能原来你的迭代器是原来的end,结果弹出左边后end改变了,两个end不同,然后你去弹出右边,访问无效迭代器,就直接RE了)
inline void Pop_right(set<Point>::iterator nxt,const Point &p) {
set<Point>::iterator lst;
while() {
lst = nxt , ++nxt;
if( nxt == st.end() ) return;
if( (*lst-p) * (*nxt-*lst) <= ) return;
st.erase(lst);
}
}
inline void Pop_left(set<Point>::iterator prv,const Point &p) {
set<Point>::iterator lst;
while(prv!=st.begin()) {
lst = prv , --prv;
if( (*lst-*prv) * (p-*lst) <= ) break;
st.erase(lst);
}
}
inline void insert(const Point &p) {
cmp = ;
set<Point>::iterator prv,nxt,lst=st.lower_bound(p);
if(lst!=st.begin()) Pop_left(--lst,p);
lst=st.lower_bound(p);
if(lst!=st.end()) Pop_right(lst,p);
st.insert(p) , lst = st.find(p);
if(lst!=st.begin()) {
prv = lst , --prv;
Point x = *prv;
x.slop = ( p.y - x.y ) / ( p.x - x.x );
st.erase(prv) , st.insert(x);
}
nxt = lst , ++nxt;
if(nxt!=st.end()) {
Point x = p , n = *nxt;
x.slop = ( n.y - x.y ) / ( n.x - x.x );
st.erase(lst) , st.insert(x);
} else {
Point x = p;
x.slop = -1e18;
st.erase(lst) , st.insert(x);
}
}
查询的话就更改一下比较函数,然后特判一下边界防止RE就好。
inline double query(const int id) {
cmp = ;
const double k = -a[id] / b[id];
set<Point>::iterator it = st.lower_bound((Point){,,k}); // it can't be st.end() if st isn't empty .
if( it==st.end() ) return ;
double ret = it->calc(a[id],b[id]);
if( it != st.begin() ) {
--it;
ret = max( ret , it->calc(a[id],b[id]) );
}
return ret;
}
所以整体代码:
Bzoj1492:
#include<cstdio>
#include<algorithm>
#include<set>
#include<cmath>
using namespace std;
const int maxn=1e5+1e2; int cmp; // 0 compare x , 1 compare slope .
struct Point {
double x,y,slop;
friend bool operator < (const Point &a,const Point &b) {
if( !cmp ) return a.x != b.x ? a.x < b.x : a.y < b.y;
return a.slop > b.slop;
}
friend Point operator - (const Point &a,const Point &b) {
return (Point){a.x-b.x,a.y-b.y};
}
friend double operator * (const Point &a,const Point &b) {
return a.x * b.y - b.x * a.y;
}
inline double calc(double a,double b) const {
return a * x + b * y;
}
};
set<Point> st;
double a[maxn],b[maxn],rate[maxn],f[maxn],ans; inline void Pop_right(set<Point>::iterator nxt,const Point &p) {
set<Point>::iterator lst;
while() {
lst = nxt , ++nxt;
if( nxt == st.end() ) return;
if( (*lst-p) * (*nxt-*lst) <= ) return;
st.erase(lst);
}
}
inline void Pop_left(set<Point>::iterator prv,const Point &p) {
set<Point>::iterator lst;
while(prv!=st.begin()) {
lst = prv , --prv;
if( (*lst-*prv) * (p-*lst) <= ) break;
st.erase(lst);
}
}
inline void insert(const Point &p) {
cmp = ;
set<Point>::iterator prv,nxt,lst=st.lower_bound(p);
if(lst!=st.begin()) Pop_left(--lst,p);
lst=st.lower_bound(p);
if(lst!=st.end()) Pop_right(lst,p);
st.insert(p) , lst = st.find(p);
if(lst!=st.begin()) {
prv = lst , --prv;
Point x = *prv;
x.slop = ( p.y - x.y ) / ( p.x - x.x );
st.erase(prv) , st.insert(x);
}
nxt = lst , ++nxt;
if(nxt!=st.end()) {
Point x = p , n = *nxt;
x.slop = ( n.y - x.y ) / ( n.x - x.x );
st.erase(lst) , st.insert(x);
} else {
Point x = p;
x.slop = -1e18;
st.erase(lst) , st.insert(x);
}
}
inline double query(const int id) {
cmp = ;
const double k = -a[id] / b[id];
set<Point>::iterator it = st.lower_bound((Point){,,k}); // it can't be st.end() if st isn't empty .
if( it==st.end() ) return ;
double ret = it->calc(a[id],b[id]);
if( it != st.begin() ) {
--it;
ret = max( ret , it->calc(a[id],b[id]) );
}
return ret;
} int main() {
static int n;
scanf("%d%lf",&n,&ans);
for(int i=;i<=n;i++) scanf("%lf%lf%lf",a+i,b+i,rate+i);
for(int i=;i<=n;i++) {
ans = max( ans , query(i) );
f[i] = ans * rate[i] / ( a[i] * rate[i] + b[i] );
insert((Point){f[i],f[i]/rate[i],});
}
printf("%0.3lf\n",ans);
return ;
}
不得不说STL的set跑的还是挺快的。
这里是回档后的世界,无论你做什么,你都一定会这样做。
而我努力改变命运,只是为了防止那一切重现。
(来自某中二病晚期患者(不))
懒人的福利?教你用set维护斜率优化凸包的更多相关文章
- 网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接
本文原作者:“水晶虾饺”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.引言 好多小白初次接触即时通讯(比如:IM或者消息推送应用)时,总是不 ...
- 【轮子狂魔】手把手教你用JS给博客动态增加目录 - 超级懒人版
动态显示目录的作用 不用每次写博客的时候繁琐的人工整理目录,又可以动态浮动在右下角,方便快速跳到感兴趣的位置同时也可以快速的对文章内容有一个大概的了解. 实现原理 首先根据个人喜好,我习惯了用 h1 ...
- 懒人邮件群发日发50-100万封不打码不换IP不需发件箱大站协议系统营销软件100%进收件箱
用一种新的技术思维去群发邮件 一种不用换IP,不需要任何发件箱的邮件群发方式 一种不需要验证码,不需要**代码变量的邮件群发方式 即使需要验证码也能全自动识别验证码的超级智能软件 教你最核心的邮件群发 ...
- Mac OS X 懒人版安装教程(之前的图全部挂了,所以重发了)
请版主把我之前发的那个帖子删了!因为所有的图全部挂了,所以麻烦版主了…… 安装中出现五国的话就请进入这里看看是那里的错误http://bbs.pcbeta.com/viewthread-863656- ...
- 详细的OS X Yosemite 10.10懒人版安装教程
永远记住一句话:难,是因为不会.先是要放宽心态,才更利于解决安装过程中这样那样的问题.多尝试多动脑,不要有过份的依赖.很多问题到解决以后,才发现是如此的简单,我装黑苹果是拿来使用的,所以我的目的是装好 ...
- 【原创】窥视懒人的秘密---android下拉刷新开启手势的新纪元
小飒的成长史原创作品:窥视懒人的秘密---android下拉刷新开启手势的新纪元转载请注明出处 **************************************************** ...
- 网络编程懒人入门(九):通俗讲解,有了IP地址,为何还要用MAC地址?
1.前言 标题虽然是为了解释有了 IP 地址,为什么还要用 MAC 地址,但是本文的重点在于理解为什么要有 IP 这样的东西.本文对读者的定位是知道 MAC 地址是什么,IP 地址是什么. (本文同步 ...
- FlytestingToolkit工具派送,懒人的测试思考
工欲善其事必先利其器,在IT路上摸爬这些年,去年我们分享了<Fiddler录制jmeter脚本,干货分享>,今天我们有另外的思考,我懒,故我思考. 下载解压后是这样的: 双击 Flytes ...
- 网络编程懒人入门(十):一泡尿的时间,快速读懂QUIC协议
1.TCP协议到底怎么了? 现时的互联网应用中,Web平台(准确地说是基于HTTP及其延伸协议的客户端/服务器应用)的数据传输都基于 TCP 协议. 但TCP 协议在创建连接之前需要进行三次握手(如下 ...
随机推荐
- Scala进阶之路-高级数据类型之集合的使用
Scala进阶之路-高级数据类型之集合的使用 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Scala 的集合有三大类:序列 Seq.集 Set.映射 Map,所有的集合都扩展自 ...
- css拾遗(一)(inline-block,absolute)
一:inline-block中不要嵌套其他block标签,不然会破坏布局 <style> .left{ float:left; } .hide{ display:none; } a{ di ...
- angular.module()参数问题
var app = angular.module('myApp', []); 第二个参数是依赖的模块,因为这里不需要依赖其它模块,因此为空,但是[]不能省略.
- SQL记录-PLSQL事务
PL/SQL事务 数据库事务是一个工作的原子单元,其可以由一个或多个相关的SQL语句组成.所谓的原子性就是数据库的修改所带来的构成事务的SQL语句可以集体被提交,即永久到数据库或从数据库中(撤消) ...
- 微软官网给出CSS选择器支持列表
CSS Compatibility and Internet Explorer 这是在 @司徒正美 博客里看到的,所以搬到自己博客,收藏下..正如司徒兄所说,微软太狡滑了,如果把不支持的属性用红色标示 ...
- python制作查找单词翻译的脚本
本人由于英语渣,在linux底下经常看文档,但是有没有想有道词典这种软件,所以遇到不懂的单词只能手动复制粘贴在网上查找,这样就很不方便,学了python之后,就试着自己尝试下个在命令行下查找单词翻译的 ...
- Flex 界面初始化 自定义 预加载 类!
说明: 自定义界面初始化过程提示:初始化...,初始化完毕,加载完毕! ZPreloader.as package com.command { import flash.display.Graphic ...
- 【洛谷 P2726】 [SHOI2005]树的双中心(树的重心)
先考虑一个\(O(N^2)\)做法. 设选的两个点为\(x,y\),则一定可以将树分成两个集合\(A,B\),使得\(A\)集合所有点都去\(x\),\(B\)集合所有点都去\(y\),而这两个集合的 ...
- [转]python ctypes 探究 ---- python 与 c 的交互
近几天使用 python 与 c/c++ 程序交互,网上有推荐swig但效果都不理想,所以琢磨琢磨了 python 的 ctypes 模块.同时,虽然网上有这方面的内容,但是感觉还是没说清楚.这里记录 ...
- 【Python】Flask系列-数据库笔记
MySQL-python中间件的介绍与安装: 1.如果是在类unix系统上,直接进入虚拟环境,输入sudo pip install mysql-python. 2.如果是在windows系统上,那么在 ...