这是道CDQ分治的例题:

$O(n^2)$的DP:

f [1]←S* Rate[1] / (A[1] * Rate[1] + B[1])
Ans←S
For i ← 2 to n
  For j ←1 to i-1
    x ← f [j] * A[i] + f [j] / Rate[j] * B[i]
    If x> Ans
      Then Ans ← x
  End For
  f [i] ← Ans* Rate[i] / (A[i] * Rate[i] + B[i])
End For
Print(Ans)

决策i是通过1~i-1之间的决策转移过来的,对于j,k∈[1,i-1]且决策k比决策j优当且仅当:

$$(f[j]-f[k])×A[i]+\frac{f[j]}{Rate[j]-\frac{f[k]}{Rate[k]}}×B[i]<0$$

不妨设$f[j]<f[k]$,$g[j]=\frac{f[j]}{Rate[j]}$,那么

$$\frac{g[j]-g[k]}{f[j]-f[k]}>-\frac{a[i]}{b[i]}$$

斜率优化DP能用单调队列维护是因为右边的斜率是单调的,但这里$-\frac{a[i]}{b[i]}$显然是不单调的,这时就需要在单调队列上二分。但因为每次插入的点的横坐标也不是单调的,我们就得建立一棵splay在线地向其中加点或询问

以f值为关键字建立splay,维护一个横坐标为f值,纵坐标为g值得上凸壳,时间复杂度为$O(n\log n)$

  1. #include<cmath>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. const double inf = 1e9;
  7. const double eps = 1e-9;
  8. const int N = 100003;
  9.  
  10. int n;
  11. double f[N], A[N], B[N], Rate[N];
  12. struct node *null;
  13. struct node {
  14. node *ch[2], *fa;
  15. double lk, rk, x, y;
  16. int id;
  17. node(int num) {ch[0] = ch[1] = fa = null; lk = rk = x = y = 0; id = num;}
  18. bool pl() {return fa->ch[1] == this;}
  19. void setc(node *r, bool c) {this->ch[c] = r; if (r != null) r->fa = this;}
  20. } *root;
  21.  
  22. namespace Splay {
  23. void rotate(node *r) {
  24. node *f = r->fa;
  25. bool c = r->pl();
  26. if (f == root) root = r, r->fa = null;
  27. else f->fa->setc(r, f->pl());
  28. f->setc(r->ch[!c], c);
  29. r->setc(f, !c);
  30. }
  31. void splay(node *r, node *tar = null) {
  32. for(; r->fa != tar; rotate(r))
  33. if (r->fa->fa != tar) rotate(r->pl() == r->fa->pl() ? r->fa : r);
  34. }
  35. node *find(double x) {
  36. if (root == null) return null;
  37. node *r = root;
  38. while (r != null) {
  39. if (r->lk < x) r = r->ch[0];
  40. else if (r->rk > x) r = r->ch[1];
  41. else if (r->lk >= x && r->rk <= x) return r;
  42. }
  43. return r;
  44. }
  45. double get(node *a, node *b) {
  46. if (fabs(a->x - b->x) < eps) return -inf;
  47. else return (b->y - a->y) / (b->x - a->x);
  48. }
  49. node *getl(node *r) {
  50. node *t = r->ch[0], *ans = t;
  51. while (t != null) {
  52. if (t->lk >= get(t, r)) ans = t, t = t->ch[1];
  53. else t = t->ch[0];
  54. }
  55. return ans;
  56. }
  57. node *getr(node *r) {
  58. node *t = r->ch[1], *ans = t;
  59. while (t != null) {
  60. if (get(r, t) >= t->rk) ans = t, t = t->ch[0];
  61. else t = t->ch[1];
  62. }
  63. return ans;
  64. }
  65. void insert(node *t) {
  66. if (root == null) {
  67. root = t;
  68. root->lk = inf;
  69. root->rk = -inf;
  70. return;
  71. }
  72. node *r = root;
  73. while (r != null) {
  74. if (t->x < r->x) {
  75. if (r->ch[0] == null) {r->setc(t, 0); break;}
  76. else r = r->ch[0];
  77. } else {
  78. if (r->ch[1] == null) {r->setc(t, 1); break;}
  79. else r = r->ch[1];
  80. }
  81. }
  82. splay(t);
  83. if (t->ch[0] != null) {
  84. node *tl = getl(t);
  85. splay(tl, root);
  86. tl->ch[1] = null;
  87. tl->rk = t->lk = get(tl, t);
  88. } else
  89. t->lk = inf;
  90. if (t->ch[1] != null) {
  91. node *tr = getr(t);
  92. splay(tr, root);
  93. tr->ch[0] = null;
  94. tr->lk = t->rk = get(t, tr);
  95. } else
  96. t->rk = -inf;
  97. if (t->lk < t->rk) {
  98. node *cl = t->ch[0], *cr = t->ch[1];
  99. if (cl == null && cr == null) //其实删点根本不用这么麻烦,不用判断左边是否为空,若左边为空那么t->lk<t->rk的判断就过不了,这是fqk打野提醒我的QAQ
  100. root = null;
  101. else if (cl == null) {
  102. root = cr;
  103. cr->fa = null;
  104. cr->lk = inf;
  105. } else if (cr == null) {
  106. root = cl;
  107. cl->fa = null;
  108. cl->rk = -inf;
  109. } else {
  110. root = cl;
  111. cl->fa = null;
  112. cl->setc(cr, 1);
  113. cl->rk = cr->lk = get(cl, cr);
  114. }
  115. }
  116. }
  117. }
  118.  
  119. int main() {
  120. scanf("%d%lf", &n, &f[0]);
  121. for(int i = 1; i <= n; ++i) scanf("%lf%lf%lf", &A[i], &B[i], &Rate[i]);
  122. null = new node(0); *null = node(0); root = null;
  123. for(int i = 1; i <= n; ++i) {
  124. node *j = Splay::find(-A[i] / B[i]);
  125. f[i] = max(f[i - 1], j->x * A[i] + j->y * B[i]);
  126. node *r = new node(i);
  127. r->y = f[i] / (A[i] * Rate[i] + B[i]);
  128. r->x = r->y * Rate[i];
  129. Splay::insert(r);
  130. }
  131. printf("%.3lf\n", f[n]);
  132. return 0;
  133. }

splay好写好调!但是还是得学CDQ分治啊!!!

CDQ分治利用预处理排序,使原先的序列的询问的斜率有序,然后处理左半边,用左半边更新右半边,再处理右半边,这样因为询问的斜率有序,就可以用单调队列或单调栈来维护了,复杂度也是$O(n\log n)$,论文里讲得很清楚啊。

  1. #include<cmath>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. const int N = 100003;
  7. const double inf = 1e9;
  8. const double eps = 1e-9;
  9. struct node {
  10. double q, a, b, rate, k; int id;
  11. } q[N], qq[N];
  12. struct Point {
  13. double x, y;
  14. bool operator < (const Point &a) const {
  15. return (x < a.x) || (fabs(x - a.x) < eps && y < a.y);
  16. }
  17. } p[N], pp[N];
  18.  
  19. double get(int x, int y) {
  20. if (fabs(p[x].x - p[y].x) < eps) return -inf;
  21. return (p[x].y - p[y].y) / (p[x].x - p[y].x);
  22. }
  23.  
  24. int st[N], n;
  25. double f[N];
  26. void solve(int l, int r) {
  27. if (l == r) {
  28. f[l] = max(f[l - 1], f[l]);
  29. p[l].y = f[l] / (q[l].a * q[l].rate + q[l].b);
  30. p[l].x = p[l].y * q[l].rate;
  31. return;
  32. }
  33. int mid = (l + r) >> 1, h = l, t = mid + 1;
  34. for(int i = l; i <= r; ++i)
  35. if (q[i].id <= mid) qq[h++] = q[i];
  36. else qq[t++] = q[i];
  37. for(int i = l; i <= r; ++i) q[i] = qq[i];
  38. solve(l, mid);
  39. int top = 0;
  40. for(int i = l; i <= mid; ++i) {
  41. while (top >= 2 && get(i, st[top]) > get(st[top], st[top - 1])) --top;
  42. st[++top] = i;
  43. }
  44. t = 1;
  45. for(int i = r; i > mid; --i) {
  46. while (t < top && q[i].k < get(st[t], st[t + 1])) ++t;
  47. f[q[i].id] = max(f[q[i].id], p[st[t]].x * q[i].a + p[st[t]].y * q[i].b);
  48. }
  49. solve(mid + 1, r);
  50. h = l; t = mid + 1;
  51. for(int i = l; i <= r; ++i)
  52. if ((p[h] < p[t] || t > r) && h <= mid) pp[i] = p[h++];
  53. else pp[i] = p[t++];
  54. for(int i = l; i <= r; ++i) p[i] = pp[i];
  55. }
  56.  
  57. bool cmp(node A, node B) {return A.k < B.k;}
  58. int main() {
  59. scanf("%d%lf", &n, &f[0]);
  60. for(int i = 1; i <= n; ++i) {
  61. scanf("%lf%lf%lf", &q[i].a, &q[i].b, &q[i].rate);
  62. q[i].k = -q[i].a / q[i].b;
  63. q[i].id = i;
  64. }
  65. sort(q + 1, q + n + 1, cmp);
  66. solve(1, n);
  67. printf("%.3lf\n", f[n]);
  68. return 0;
  69. }

省队集训期间我充分展现出了自己的弱QAQ(为什么不说发现自己的弱呢,因为我很久以前就发现了TwT)继续努力~后天就要回新校颓文化课了,下一个月好好搞文化课,年级里的排名不能再退步了。希望期末考试能取得较大的进步,毕竟这是我第一次准备认认真真颓文化课QwQ←滚粗狗的无奈

2016-07-11期末挂惨了TwT,比以前任何一次都惨_(:з」∠)_之前的flag too naive!

【BZOJ 1492】【NOI 2007】货币兑换Cash的更多相关文章

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

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

  2. 【bzoj 1492】[NOI2007]货币兑换Cash

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

  3. [NOI 2007]货币兑换Cash

    Description 题库链接 (按我的语文水平完全无 fa♂ 概括题意,找了 hahalidaxin 的题意简述... 有 \(AB\) 两种货币,每天可以可以付 \(IP_i\) 元,买到 \( ...

  4. 【BZOJ 1492】 [NOI2007]货币兑换Cash 斜率优化DP

    先说一下斜率优化:这是一种经典的dp优化,是OI中利用数形结合的思想解决问题的典范,通常用于优化dp,有时候其他的一些决策优化也会用到,看待他的角度一般有两种,但均将决策看为二维坐标系上的点,并转化为 ...

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

  6. 【BZOJ】【1492】【NOI207】货币兑换Cash

    DP/CDQ分治 orz Hzwer copy了下他的代码……结果在while(j<top......)这一句中把一个括号的位置打错了……找了我一个多小时才找到TAT 很神奇……顺便贴下CDQ的 ...

  7. bzoj千题计划237:bzoj1492: [NOI2007]货币兑换Cash

    http://www.lydsy.com/JudgeOnline/problem.php?id=1492 dp[i] 表示 第i天卖完的最大收益 朴素的dp: 枚举从哪一天买来的在第i天卖掉,或者是不 ...

  8. 1492: [NOI2007]货币兑换Cash【CDQ分治】

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

  9. 【BZOJ-1492】货币兑换Cash DP + 斜率优化 + CDQ分治

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

  10. [NOI2007 Day1] 货币兑换 Cash

    vijos P1508 / BZOJ 1492 膜拜了这么久的cdq分治,终于有机会亲自来写了.虽然这个思想很好理解,先做前一半,计算前一半对后一半的影响,再做后一半.但是由于我这个傻Ⅹ,以前既没有做 ...

随机推荐

  1. 我的Github之旅(一)

    第一站:本地环境中的Github配置 1.参考链接 作为初学者,需要了解的有[本地环境中的github配置(基于mac)][1],以及git知识,这里推荐一个网站[猴子都能懂的Git入门][2],最后 ...

  2. instanceof

    java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例. instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例. result ...

  3. Flash 二进制传图片到后台Java服务器接收

    需求:把客户端处理过的图片返还给服务器Flash端代码 01 package {02     import com.adobe.images.JPGEncoder;    03     import  ...

  4. java 22 - 19 多线程之生产者和消费者的代码优化

    在之前,是把生产者录入数据和消费者获取数据的所有代码都分别写在各自的类中. 这样不大好 这次把生产者和消费者部分关键代码都写入资源类中: package zl_Thread; public class ...

  5. Eclipse的 JSON Edit插件

    1. Json-Eclipse-Plugin https://github.com/boothen/Json-Eclipse-Plugin 2. 另外一个JSON Edit工具 https://tfe ...

  6. tshark过滤并保存包特定字段

    1.过滤端口为5001的tcp包,将时间输出 tshark -r h1.pcap -Y "tcp.port==5001" -T fields -e frame.time 时间格式如 ...

  7. 最大M子段和 V2

    51nod1053 这题还是我们熟悉的M子段和,只不过N,M<=50000. 这题似乎是一个堆+链表的题目啊 开始考虑把所有正数负数锁在一起. 比如: 1 2 3 -1 –2 -3 666 缩成 ...

  8. js函数命名常用动词

    get 获取/set 设置, add 增加/remove 删除 create 创建/destory 移除 start 启动/stop 停止 open 打开/close 关闭, read 读取/writ ...

  9. Centos5.8下编译安装PHP5.4和memcached, phalcon, yaf, apc

    安装GIT 需要先安装gcc-c++ (sudo yum install gcc-c++)sudo yum install gettext-devel expat-devel cpio perl op ...

  10. Spring Security授权 AccessDecisionManager

    Spring Security授权 AccessDecisionManager 博客分类: Security Spring   在前面那篇博客有一段配置: <http auto-config=& ...