题意:

有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无误;

代码:

  1. #include<math.h>
  2. #include<stdio.h>
  3. #include<string.h>
  4. #include<algorithm>
  5. #define N 110000
  6. #define which(x) (tr[tr[x].fa].ch[1]==x)
  7. const double INF = 1e100;
  8. const double EPS = 1e-8;
  9. using namespace std;
  10. struct Point
  11. {
  12. double x, y, s1, s2;
  13. int fa, ch[2];
  14. }tr[N];
  15. int root, tot;
  16. double f[N], A[N], B[N], R[N], X[N], Y[N];
  17. void get_slope(int a, int b)
  18. {
  19. if (!a) tr[b].s1 = INF;
  20. else if (!b) tr[a].s2 = -INF;
  21. else
  22. {
  23. if (fabs(tr[a].x - tr[b].x)<EPS)
  24. tr[a].s2 = tr[b].s1 = (tr[a].y<tr[b].y ? INF : -INF);
  25. else
  26. tr[a].s2 = tr[b].s1 = (tr[a].y - tr[b].y) / (tr[a].x - tr[b].x);
  27. }
  28. }
  29. void Rotate(int x)
  30. {
  31. int f = tr[x].fa;
  32. if (!f) return;
  33. bool k = which(x);
  34. tr[f].ch[k] = tr[x].ch[!k];
  35. tr[x].ch[!k] = f;
  36. tr[tr[f].fa].ch[which(f)] = x;
  37. tr[x].fa = tr[f].fa;
  38. tr[tr[f].ch[k]].fa = f;
  39. tr[f].fa = x;
  40. }
  41. void Splay(int x, int g)
  42. {
  43. if (!x) return;
  44. while (tr[x].fa != g)
  45. {
  46. int f = tr[x].fa;
  47. if (tr[f].fa == g)
  48. {
  49. Rotate(x);
  50. break;
  51. }
  52. if (which(x) ^ which(f))
  53. Rotate(x);
  54. else
  55. Rotate(f);
  56. Rotate(x);
  57. }
  58. if (!g) root = x;
  59. }
  60. int Pre(int x)
  61. {
  62. if (!x) return 0;
  63. int p = tr[x].ch[0];
  64. if (!p) return 0;
  65. while (tr[p].ch[1])
  66. p = tr[p].ch[1];
  67. return p;
  68. }
  69. int Sub(int x)
  70. {
  71. if (!x) return 0;
  72. int p = tr[x].ch[1];
  73. if (!p) return 0;
  74. while (tr[p].ch[0])
  75. p = tr[p].ch[0];
  76. return p;
  77. }
  78. int find(int p, double x)
  79. {
  80. if (!p) return 0;
  81. if (x<tr[p].x)
  82. return find(tr[p].ch[0], x);
  83. else
  84. {
  85. int t = find(tr[p].ch[1], x);
  86. return tr[p].x>tr[t].x ?
  87.  
  88. p : t;
  89. }
  90. }
  91. void Insert(double X, double Y, int no)
  92. {
  93. int x = find(root, X), y = 0;
  94. if (!x)
  95. {
  96. x = root;
  97. while (tr[x].ch[0])
  98. x = tr[x].ch[0];
  99. Splay(x, 0);
  100. y = x, x = 0;
  101. }
  102. else
  103. {
  104. Splay(x, 0);
  105. Splay(y = Sub(x), x);
  106. }
  107. tr[no].x = X, tr[no].y = Y;
  108. if (y) tr[no].fa = y, tr[y].ch[0] = no;
  109. else tr[no].fa = x, tr[x].ch[1] = no;
  110. get_slope(x, no);
  111. get_slope(no, y);
  112. if (tr[no].s1 <= tr[no].s2)
  113. {
  114. tr[y].ch[0] = 0;
  115. get_slope(x, y);
  116. return;
  117. }
  118. Rotate(no), Rotate(no);
  119. root = no;
  120. x = tr[no].ch[0];
  121. while (tr[x].s1 <= tr[x].s2&&x)
  122. {
  123. y = Pre(x);
  124. Splay(y, x);
  125. tr[y].fa = no;
  126. tr[no].ch[0] = y;
  127. get_slope(y, no);
  128. x = y;
  129. }
  130. x = tr[no].ch[1];
  131. while (tr[x].s1 <= tr[x].s2&&x)
  132. {
  133. y = Sub(x);
  134. Splay(y, x);
  135. tr[y].fa = no;
  136. tr[no].ch[1] = y;
  137. get_slope(no, y);
  138. x = y;
  139. }
  140. }
  141. int query(double S)
  142. {
  143. int p = root;
  144. while (S>tr[p].s1 || S<tr[p].s2)
  145. {
  146. if (S>tr[p].s1) p = tr[p].ch[0];
  147. else p = tr[p].ch[1];
  148. }
  149. return p;
  150. }
  151. int main()
  152. {
  153. int n, i, j, k;
  154. scanf("%d%lf", &n, &f[1]);
  155. for (i = 1; i <= n; i++)
  156. scanf("%lf%lf%lf", A + i, B + i, R + i);
  157. tr[0].x = tr[0].y = -INF;
  158. Y[1] = f[1] / (A[1] * R[1] + B[1]);
  159. X[1] = R[1] * Y[1];
  160. Insert(X[1], Y[1], 1);
  161. for (i = 2; i <= n; i++)
  162. {
  163. j = query(-A[i] / B[i]);
  164. f[i] = max(f[i - 1], A[i] * X[j] + B[i] * Y[j]);
  165. Y[i] = f[i] / (A[i] * R[i] + B[i]);
  166. X[i] = R[i] * Y[i];
  167. Insert(X[i], Y[i], i);
  168. }
  169. printf("%.3lf", f[n]);
  170. return 0;
  171. }

bzoj-1492 货币兑换Cash (1)——平衡树维护凸包的更多相关文章

  1. BZOJ 1492 货币兑换Cash

    http://www.lydsy.com/JudgeOnline/problem.php?id=1492 思路: 问题转变为维护一个凸包,每次转移都找凸包上的点,并更新凸壳 可以用splay维护,或者 ...

  2. BZOJ 1492 货币兑换 Cash CDQ分治

    这题n2算法就是一个维护上凸包的过程. 也可以用CDQ分治做. 我的CDQ分治做法和网上的不太一样,用左边的点建立一个凸包,右边的点在上面二分. 好处是思路清晰,避免了凸包的插入删除,坏处是多了一个l ...

  3. BZOJ 1492 货币兑换 cdq分治或平衡树维护凸包

    题意:链接 方法:cdq分治或平衡树维护凸包 解析: 这道题我拒绝写平衡树的题解,我仅仅想说splay不要写挂,insert边界条件不要忘.del点的时候不要脑抽d错.有想写平衡树的去看140142或 ...

  4. 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的数量. 然后就很明显了...平衡 ...

  5. [BZOJ1492] [NOI2007]货币兑换Cash 斜率优化+cdq/平衡树维护凸包

    1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 5907  Solved: 2377[Submit][Sta ...

  6. BZOJ 1492 货币兑换

    Description Input 第一行两个正整数\(N,S\),分别表示小Y 能预知的天数以及初始时拥有的钱数. 接下来\(N\)行,第\(K\)行三个实数\(A_{K},B_{K},Rate_{ ...

  7. [NOI2007]货币兑换Cash(DP+动态凸包)

    第一次打动态凸包维护dp,感觉学到了超级多的东西. 首先,set是如此的好用!!!可以通过控制一个flag来实现两种查询,维护凸包和查找斜率k 不过就是重载运算符和一些细节方面有些恶心,90行解决 后 ...

  8. BZOJ 1492 [NOI2007] - cash

    Description 最初你有 S 块钱, 有 N 天给你来兑换货币, 求最大获利. 一共只有两种货币 A , B . 对于每一天, 给定 3 个系数 A[i], B[i], Rate[i] A[i ...

  9. NOI 2007 货币兑换Cash (bzoj 1492) - 斜率优化 - 动态规划 - CDQ分治

    Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下 简称B券).每个持有金券的顾客都有一个自己的帐户.金券的数目可以是一个 ...

随机推荐

  1. POJ 1811

    使用Pollard_rho算法就可以过了 #include <iostream> #include <cstdio> #include <algorithm> #i ...

  2. coalesce函数-返回参数中第一个非null值

    coalesce函数-返回参数中第一个非null值 学习了:http://www.cnblogs.com/zc_0101/archive/2009/08/11/1543650.html 这个要复杂一些 ...

  3. [Spring实战系列](17)编写切点与声明切面

    切点用于准确定位应该在什么地方应用切面的通知. 切点和通知是切面的最基本元素. 在Spring AOP中,须要使用AspectJ的切点表达式语言来定义切点. 关于Spring AOP的AspectJ切 ...

  4. hdu 5335 Walk Out (2015 Multi-University Training Contest 4)

    Walk Out                                                                         Time Limit: 2000/10 ...

  5. 利用js在文本框末尾获得焦点

    function moveEnd(obj) { obj.focus(); var len = obj.value.length; if (document.selection) { var sel = ...

  6. MFC 与Excel文件的交互操作

    假日快要结束了.带着沉重的心情写下之前关于MFC与Excel文件交互的总结. 因为VS的版本号不同可能在操作上有些差异.所以在此指明下本篇文章的project环境为VS2013,也建议大家用最新的. ...

  7. Windows系统安装MySQL5.5.21图解教程

    大家都知道MySQL是一款中.小型关系型数据库管理系统,很具有实用性,对于我们学习很多技术都有帮助 数据库是5.5.21这个版本的.以下是安装步骤: 1.首先单击MySQL5.5.21的安装文件,出现 ...

  8. myeclipse打开jsp页面慢或者卡死

    不知道你们有没有这种经历,反正无论是公司电脑还是自己电脑,myeclipse打开jsp页面卡的不行不行的,又是甚至会出现卡死的现象,几经周折,找到了解决办法,亲测有效 打开window-prefere ...

  9. shp系列(一)——利用C++进行shp文件的读(打开)与写(创建)开言

    博客背景和目的 最近在用C++写一个底层的东西,需要读取和创建shp文件.虽然接触shp文件已经几年了,但是对于shp文件内到底包含什么东西一直是一知半解.以前使用shp文件都是利用软件(如ArcGI ...

  10. 绘图中的drawRect

    rect参数:代表的是当前view的bounds 1 为什么要在drawRect方法里面写绘图代码 因为只有在这个方法中才能获取到当前view相关的图形上下文对象 有了这个图形上写文对象后才能进行绘图 ...