bzoj-1492 货币兑换Cash (1)——平衡树维护凸包
题意:
有n天和m的初始金钱,用来购买AB两种纪念券;
n天里每天都有AB的价格。每天能够进行这种操作。
1.卖出手中x%的纪念券(AB分别都卖出x%)。
2.用x的金钱买入纪念券。买入AB券的比例在第i天为Rate i;
求n天过去之后所获得的最大收益。
金钱和券数均为实数;
n<=100 000;
题解:
首先,尽管题中的买入和卖出都是随意数量的。可是相同的纪念券,分几天卖出得到的收 益。一定小于等于直接在一天卖出的收益;
相同。分几天买入也是不如一天花全部钱买入的;
令:
f[i]为第i天的最大收益。
X[i]为第i天将全部钱数都买入得到的A券数。
Y[i]为第i天将全部钱数都买入得到的B券数;
显然X,Y都能够由f[i]得来。
那么转移方程就是:
f[i]=max(f[i-1],A[i] * X[j] + B[i] * Y[j]);(1<=j< i);
这样转移是O(n^2)的。所以要对后面枚举j的部分优化;
将f[i]=A[i] * X[j] + B[i] * Y[j]整理;
得到Y[i]=(-A[i]/B[i]) * X[i] + f[i]/B[i];
这是一个直线方程的形式,而对于固定的i,直线斜率不变,而要最大化截距;
倘若将全部的[1,i-1]的点计算出(x,y)放在坐标系上;
找到最优值相当于在这个上凸包上找到某个点。使截距最大。
那么这个点左面的斜率一定大于当前i的斜率,右面的斜率一定小于当前i的斜率。
所以这事实上就是一个斜率优化的形式;
通常我们做斜率优化都是找到不符合要求的点直接干掉就好的;
由于下一个i的斜率不是递增就是递减;
可是这个-A[i]/B[i]不单调,所以不能O(n)的维护队列处理凸包;
10^5的复杂度是支持O(nlogn)的,所以为了维护凸包能够选择一些数据结构;
那么就维护一个Splay,每一个结点都在凸包上,中序遍历就是按x递增同一时候也按斜率递减的序列;
假设把Splay看做logn,那么每一个点最多进出凸包一次。二分查询斜率共n次。
复杂度O(nlogn)还是听起来非常好的;
可是写起来一点也不好玩!
总之维护凸包时对Splay中点较少时的讨论非常烦。。。
照着对拍调数据改改也就过了。
代码3k+,时间960ms,这个跑的感觉也是挺快的了;
下一篇写写更神的CDQ分治,毕竟数据结构对代码能力要求颇高。
7/17
我被D了。。
。斜率打成了shope,恩应该是slope无误;
代码:
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define which(x) (tr[tr[x].fa].ch[1]==x)
const double INF = 1e100;
const double EPS = 1e-8;
using namespace std;
struct Point
{
double x, y, s1, s2;
int fa, ch[2];
}tr[N];
int root, tot;
double f[N], A[N], B[N], R[N], X[N], Y[N];
void get_slope(int a, int b)
{
if (!a) tr[b].s1 = INF;
else if (!b) tr[a].s2 = -INF;
else
{
if (fabs(tr[a].x - tr[b].x)<EPS)
tr[a].s2 = tr[b].s1 = (tr[a].y<tr[b].y ? INF : -INF);
else
tr[a].s2 = tr[b].s1 = (tr[a].y - tr[b].y) / (tr[a].x - tr[b].x);
}
}
void Rotate(int x)
{
int f = tr[x].fa;
if (!f) return;
bool k = which(x);
tr[f].ch[k] = tr[x].ch[!k];
tr[x].ch[!k] = f;
tr[tr[f].fa].ch[which(f)] = x;
tr[x].fa = tr[f].fa;
tr[tr[f].ch[k]].fa = f;
tr[f].fa = x;
}
void Splay(int x, int g)
{
if (!x) return;
while (tr[x].fa != g)
{
int f = tr[x].fa;
if (tr[f].fa == g)
{
Rotate(x);
break;
}
if (which(x) ^ which(f))
Rotate(x);
else
Rotate(f);
Rotate(x);
}
if (!g) root = x;
}
int Pre(int x)
{
if (!x) return 0;
int p = tr[x].ch[0];
if (!p) return 0;
while (tr[p].ch[1])
p = tr[p].ch[1];
return p;
}
int Sub(int x)
{
if (!x) return 0;
int p = tr[x].ch[1];
if (!p) return 0;
while (tr[p].ch[0])
p = tr[p].ch[0];
return p;
}
int find(int p, double x)
{
if (!p) return 0;
if (x<tr[p].x)
return find(tr[p].ch[0], x);
else
{
int t = find(tr[p].ch[1], x);
return tr[p].x>tr[t].x ? p : t;
}
}
void Insert(double X, double Y, int no)
{
int x = find(root, X), y = 0;
if (!x)
{
x = root;
while (tr[x].ch[0])
x = tr[x].ch[0];
Splay(x, 0);
y = x, x = 0;
}
else
{
Splay(x, 0);
Splay(y = Sub(x), x);
}
tr[no].x = X, tr[no].y = Y;
if (y) tr[no].fa = y, tr[y].ch[0] = no;
else tr[no].fa = x, tr[x].ch[1] = no;
get_slope(x, no);
get_slope(no, y);
if (tr[no].s1 <= tr[no].s2)
{
tr[y].ch[0] = 0;
get_slope(x, y);
return;
}
Rotate(no), Rotate(no);
root = no;
x = tr[no].ch[0];
while (tr[x].s1 <= tr[x].s2&&x)
{
y = Pre(x);
Splay(y, x);
tr[y].fa = no;
tr[no].ch[0] = y;
get_slope(y, no);
x = y;
}
x = tr[no].ch[1];
while (tr[x].s1 <= tr[x].s2&&x)
{
y = Sub(x);
Splay(y, x);
tr[y].fa = no;
tr[no].ch[1] = y;
get_slope(no, y);
x = y;
}
}
int query(double S)
{
int p = root;
while (S>tr[p].s1 || S<tr[p].s2)
{
if (S>tr[p].s1) p = tr[p].ch[0];
else p = tr[p].ch[1];
}
return p;
}
int main()
{
int n, i, j, k;
scanf("%d%lf", &n, &f[1]);
for (i = 1; i <= n; i++)
scanf("%lf%lf%lf", A + i, B + i, R + i);
tr[0].x = tr[0].y = -INF;
Y[1] = f[1] / (A[1] * R[1] + B[1]);
X[1] = R[1] * Y[1];
Insert(X[1], Y[1], 1);
for (i = 2; i <= n; i++)
{
j = query(-A[i] / B[i]);
f[i] = max(f[i - 1], A[i] * X[j] + B[i] * Y[j]);
Y[i] = f[i] / (A[i] * R[i] + B[i]);
X[i] = R[i] * Y[i];
Insert(X[i], Y[i], i);
}
printf("%.3lf", f[n]);
return 0;
}
bzoj-1492 货币兑换Cash (1)——平衡树维护凸包的更多相关文章
- BZOJ 1492 货币兑换Cash
http://www.lydsy.com/JudgeOnline/problem.php?id=1492 思路: 问题转变为维护一个凸包,每次转移都找凸包上的点,并更新凸壳 可以用splay维护,或者 ...
- BZOJ 1492 货币兑换 Cash CDQ分治
这题n2算法就是一个维护上凸包的过程. 也可以用CDQ分治做. 我的CDQ分治做法和网上的不太一样,用左边的点建立一个凸包,右边的点在上面二分. 好处是思路清晰,避免了凸包的插入删除,坏处是多了一个l ...
- BZOJ 1492 货币兑换 cdq分治或平衡树维护凸包
题意:链接 方法:cdq分治或平衡树维护凸包 解析: 这道题我拒绝写平衡树的题解,我仅仅想说splay不要写挂,insert边界条件不要忘.del点的时候不要脑抽d错.有想写平衡树的去看140142或 ...
- BZOJ 1492: [NOI2007]货币兑换Cash( dp + 平衡树 )
dp(i) = max(dp(i-1), x[j]*a[i]+y[j]*b[i]), 0<j<i. x, y表示某天拥有的最多钱去买金券, 金券a和金券b的数量. 然后就很明显了...平衡 ...
- [BZOJ1492] [NOI2007]货币兑换Cash 斜率优化+cdq/平衡树维护凸包
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5907 Solved: 2377[Submit][Sta ...
- BZOJ 1492 货币兑换
Description Input 第一行两个正整数\(N,S\),分别表示小Y 能预知的天数以及初始时拥有的钱数. 接下来\(N\)行,第\(K\)行三个实数\(A_{K},B_{K},Rate_{ ...
- [NOI2007]货币兑换Cash(DP+动态凸包)
第一次打动态凸包维护dp,感觉学到了超级多的东西. 首先,set是如此的好用!!!可以通过控制一个flag来实现两种查询,维护凸包和查找斜率k 不过就是重载运算符和一些细节方面有些恶心,90行解决 后 ...
- BZOJ 1492 [NOI2007] - cash
Description 最初你有 S 块钱, 有 N 天给你来兑换货币, 求最大获利. 一共只有两种货币 A , B . 对于每一天, 给定 3 个系数 A[i], B[i], Rate[i] A[i ...
- NOI 2007 货币兑换Cash (bzoj 1492) - 斜率优化 - 动态规划 - CDQ分治
Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个 ...
随机推荐
- [HTML 5] aria-hidden
You want to use aria-hidden to prevent screen reader to access some content should be hidden from us ...
- TeamTalk Android代码分析(业务流程篇)
TeamTalk Android代码分析(业务流程篇) 1.1 总体结构 1.总体结构有点类似MVC的感觉,模块结构从上向下大体是: UI层:Activity和Fragment构成,期间包括常用的一些 ...
- android AppWidget的使用以及利用TimerTask实现widget的定时更新
第一步:首先是Widget的定义声明: 在资源文件下的xml目录中建立文件example_appwidget_info.xml: <?xml version="1.0" en ...
- SpringMVC-Interceptor拦截Session登录
背景: 开发的项目都须要账号password登录才干够查看站点的内容,所以我们设计时须要考虑,用户进入站点仅仅能从一个我们设计的规范通道进入即通过注冊的账号password登录,其它方法都是非法的和不 ...
- JAVA Excel API学习案例
先贴代码吧,执行一下.看看效果,然后看看凝视,再看看代码后面的基础介绍 创建一个新excel并写入数据: public static void myExcel2() throws IOExceptio ...
- angularjs1-1
<!DOCTYPE html> <html> <body> <header> <meta http-equiv="Content-Typ ...
- javascript系列-class12.事件
1.默认行为 什么是默认行为:默认行为就是浏览器自己触发的事件.比如:a链接的跳转,form提交时的跳转,鼠标右键跳转: oncontexmenu当点击右键菜单的时候: re ...
- Sort和UnSort的小技巧
Sort和UnSort的小技巧: 记录sortidx,对sortidx再从小到大排序就可以得到用于还原的unsortidx. 对于序列A: sort_idx = np.argsort(A) un_so ...
- [转]Linux+XAMPP+eolinker开源版v3.2.4
eolinker是一个由国人开源的接口管理系统(AMS),特性及介绍详见开源中国-eolinker首页. 搭建步骤参考:eolinker开源指南 系统环境:CentOS Linux release 7 ...
- caffe mnist实例 --lenet_train_test.prototxt 网络配置详解
1.mnist实例 ##1.数据下载 获得mnist的数据包,在caffe根目录下执行./data/mnist/get_mnist.sh脚本. get_mnist.sh脚本先下载样本库并进行解压缩,得 ...