zoukankan      html  css  js  c++  java
  • 【BZOJ1492】货币兑换Cash(CDQ分治)

    题意:

    小 Y 最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A 纪 
    念券(以下简称 A 券)和 B 纪念券(以下简称 B 券)。每个持有金券的顾客都有 
    一个自己的帐户。金券的数目可以是一个实数。  
       每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券 
    当天可以兑换的人民币数目。我们记录第 K 天中 A 券和 B 券的价值分别为 AK 和 
    BK (元/单位金券)。  
       为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。 
    比例交易法分为两个方面:  
           a)   卖出金券:顾客提供一个[0,100]内的实数OP作为卖出比例,其意 
       义为:将OP%的A券和OP%的B券以当时的价值兑换为人民币;  
           b)   买入金券:顾客支付IP元人民币,交易所将会兑换给用户总价值为  
       IP的金券,并且,满足提供给顾客的A券和B券的比例在第K天恰好为RateK;  
         
       例如,假定接下来3天内的Ak 、Bk、Ratek 的变化分别为:  

    时间                  Ak                  Bk                Ratek
    第一天                 1                  1                   1  
    第二天                 1                  2                   2  
    第三天                 2                  2                   3  
       假定在第一天时,用户手中有100元人民币但是没有任何金券。  
       用户可以执行以下的操作:  
    时间               用户操作            人民币(元)         A券的数量           B券的数量  
    开户               无                        100                  0                   0  
    第一天             买入100元                   0                 50                  50  
    第二天             卖出50%                    75                 25                  25  
    第二天             买入60元                   15                 55                  40  
    第三天             卖出100%                  205                  0                   0  

       注意到,同一天内可以进行多次操作。  
       小 Y 是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经 
    知道了未来 N 天内的 A 券和 B 券的价值以及 Rate。他还希望能够计算出来,如 
    果开始时拥有S元钱,那么N天后最多能够获得多少元钱。  

    思路:转载自http://www.cnblogs.com/zig-zag/archive/2013/04/24/3039418.html

    首先分析一下题目,对于任意一天,一定是贪心地买入所有货币或者卖出所有货币是最优的,因为有便宜我们就要尽量去占,有亏损就一点也不去碰。于是我们得到方程:

    f[i]=max{f[j]/(a[j]*rate[j]+b[j])*rate[j]*a[i]+f[j]/(a[j]*rate[j]+b[j])*b[i]}

    其中,x[j]=f[j]/(a[j]*rate[j]+b[j])*rate[j]表示第j天最多可以拥有的A货币的数量

       y[j]=f[j]/(a[j]*rate[j]+b[j])表示第j天最多可以拥有的B货币的数量

    那么方程可化简为f[i]=max{x[j]*a[i]+y[j]*b[i]},那么我们就是要选择一个最优的决策点(x[j],y[j])来更新f[i]得到最优解。

    变形:y[j]=f[i]/b[i]-x[j]*a[i]/b[i],这是一个直线的斜截式方程,由于我们是用j去更新i,那么就相当于每次用一条斜率为-a[i]/b[i]的直线去切由若干(x[j],y[j])点组成的集合,能得到的最大截距的点,就是最优决策点,进一步,就是要维护一个由若干(x[j],y[j])点组成凸壳,因为最优决策点一定在凸壳上。

    但是对于斜率-a[i]/b[i]和点(x[j],y[j])都是无序的,于是我们只能用一棵平衡树来维护凸壳,每次找到斜率能卡到的点(此点左侧的斜率和右侧的斜率恰好夹住-a[i]/b[i]斜率)。

     

    具体splay实现:我们维护x坐标递增的点集,每次把新点插入到相应位置,更新凸壳的时候,分别找到新点左右能与它组成新的凸壳的点,把中间的点删掉;如果这个点完全在旧的凸壳内,那么把这个点删掉。每次找最优决策点的时候就拿-a[i]/b[i]去切凸壳就行了。

     

    但这样搞实在是麻烦了许多,而且许多人得splay代码非常的长,在考场上就非常不容易写出来,于是出现了神一般的陈丹琪分治!

    这个神级分治的精髓在于:变在线为离线,化无序为有序。

    上面我们分析了,因为点和斜率都不是单调的,所以我们只能用一棵平衡树去维护。我们考虑导致无序的原因,是我们按照顺序依次回答了1..n的关于f值的询问。但是事实上我们并没有必要这么做,因为每个1..n的f[i]值,可能成为最优决策点一定在1..i范围内,而对于每个在1..i范围内的决策点,一定都有机会成为i+1..n的f值得最优决策点。这样1..i的f值一定不会受1..i的决策点的影响,i+1..n的点一定不会i+1..n的f值。于是可以分治!

    对于一个分治过程solve(l,r),我们用l..mid的决策点去更新mid+1..r这部分的f值,这样递归地更新的话,我们一定可以保证在递归到i点的时候,1..i-1的点都已经更新过i点的f值了。我们看到,分治的过程中,左半区(l..mid)和右半区(mid+1..r)这两个区间的作用是不同的,我们要用左半区已经更新好的f值去求出点(x,y),然后用右半区的斜率去切左半区的点集更新f值。对于左半边我们需要的只是点(x,y),右半区我们需要的只是斜率-a[i]/b[i],两部分的顺序互不影响。于是,我们在处理好左半边的东西的时候保证点集按坐标排好序,在处理右半区之前保证询问按照斜率排好序,这样相当于用一系列连续变化的直线去切一些连续点组成的凸壳,那么我们就可以简单地用一个栈来维护连续点组成的凸壳,用扫描的方法更新f值。我们一开始就排好询问的顺序,然后保证在solve之前还原左半区询问集合的顺序,这样就保证了按照原顺序得到f值;在solve之后把两部分点集归并,这样就保证了每个过程中的点集是有序的。

      1 const eps=1e-9;
      2 type arr=record
      3           x,y,rate,a,b,k:double;
      4           id:longint;
      5          end;
      6 
      7 var p,q:array[0..300000]of arr;
      8     stack:array[0..300000]of longint;
      9     dp:array[0..300000]of double;
     10     n,m,i,top:longint;
     11 
     12 function fabs(x:double):double;
     13 begin
     14  if x<0 then exit(-x);
     15  exit(x);
     16 end;
     17 
     18 function slope(x,y:longint):double;
     19 begin
     20  if fabs(y)<eps then exit(-1e20);
     21  if fabs(q[x].x-q[y].x)<eps then exit(1e20);
     22  exit((q[x].y-q[y].y)/(q[x].x-q[y].x));
     23 end;
     24 
     25 function max(x,y:double):double;
     26 begin
     27  if x>y then exit(x);
     28  exit(y);
     29 end;
     30 
     31 procedure swap(var x,y:arr);
     32 var t:arr;
     33 begin
     34  t:=x; x:=y; y:=t;
     35 end;
     36 
     37 procedure cdq(l,r:longint);
     38 var mid,l1,r1,i,j:longint;
     39 begin
     40  if l=r then
     41  begin
     42   dp[l]:=max(dp[l],dp[l-1]);
     43   q[l].y:=dp[l]/(q[l].rate*q[l].a+q[l].b);
     44   q[l].x:=q[l].y*q[l].rate;
     45   exit;
     46  end;
     47  mid:=(l+r)>>1;
     48  l1:=l; r1:=mid+1;
     49  for i:=l to r do
     50   if q[i].id<=mid then
     51   begin
     52    p[l1]:=q[i]; inc(l1);
     53   end
     54    else
     55    begin
     56     p[r1]:=q[i]; inc(r1);
     57    end;
     58  for i:=l to r do q[i]:=p[i];
     59  cdq(l,mid);
     60  top:=0;
     61  for i:=l to mid do
     62  begin
     63   while (top>1)and
     64         (slope(stack[top-1],stack[top])<slope(stack[top-1],i)+eps) do dec(top);
     65   inc(top);
     66   stack[top]:=i;
     67  end;
     68  inc(top); stack[top]:=0; j:=1;
     69  for i:=mid+1 to r do
     70  begin
     71   while (j<top)and(slope(stack[j],stack[j+1])+eps>q[i].k) do inc(j);
     72   dp[q[i].id]:=max(dp[q[i].id],q[stack[j]].x*q[i].a+q[stack[j]].y*q[i].b);
     73  end;
     74  cdq(mid+1,r);
     75  l1:=l; r1:=mid+1;
     76  for i:=l to r do
     77   if (((q[l1].x<q[r1].x)or((fabs(q[l1].x-q[r1].x)<eps)and
     78      (q[l1].y<q[r1].y)))or(r1>r))and(l1<=mid) then
     79      begin
     80       p[i]:=q[l1]; inc(l1);
     81      end
     82       else
     83       begin
     84        p[i]:=q[r1]; inc(r1);
     85       end;
     86  for i:=l to r do q[i]:=p[i];
     87 
     88 end;
     89 
     90 procedure qsort(l,r:longint);
     91 var i,j:longint;
     92     mid:double;
     93 
     94 begin
     95  i:=l; j:=r; mid:=q[(l+r)>>1].k;
     96  repeat
     97   while mid<q[i].k do inc(i);
     98   while mid>q[j].k do dec(j);
     99   if i<=j then
    100   begin
    101    swap(q[i],q[j]);
    102    inc(i); dec(j);
    103   end;
    104  until i>j;
    105  if l<j then qsort(l,j);
    106  if i<r then qsort(i,r);
    107 end;
    108 
    109 begin
    110  assign(input,'bzoj1492.in'); reset(input);
    111  assign(output,'bzoj1492.out'); rewrite(output);
    112  readln(n,dp[0]);
    113  for i:=1 to n do
    114  begin
    115   read(q[i].a,q[i].b,q[i].rate);
    116   q[i].k:=-q[i].a/q[i].b;
    117   q[i].id:=i;
    118  end;
    119  qsort(1,n);
    120  cdq(1,n);
    121  writeln(dp[n]:0:3);
    122  close(input);
    123  close(output);
    124 end.
  • 相关阅读:
    HDU 1754 I Hate It (线段树)
    HDU 1394 Minimum Inversion Number (树状数组)
    天梯赛 L2-012 关于堆的判断 (二叉树)
    HDU 1166 敌兵布阵 (树状数组 单点修改+区间查询)
    [leetcode-77-Combinations]
    [leetcode-101-Symmetric Tree]
    [leetcode-21-Merge Two Sorted Lists]
    [leetcode-109-Convert Sorted List to Binary Search Tree]
    [leetcode-507-Perfect Number]
    [leetcode-537-Complex Number Multiplication]
  • 原文地址:https://www.cnblogs.com/myx12345/p/6440906.html
Copyright © 2011-2022 走看看