[BZOJ1492][NOI2007]货币兑换 斜率优化DP+splay维护凸壳

写着题写的想死。。。来扯扯写着题的过程。。。(我菜啊)
早晨总结了一下昨天写的几道决策单调性和斜率优化dp,然后突然看到这题,准备来写,此时8:30。
看了popoQQQ的题解,感觉这题代码有点难度,不过还是要挑战一下的,然后大概理了理思路和细节,就开始码了,此时9:00。
10:40码完,调了几个splayRE的地方,11:00过了样例(样例很水啊),交一发,TLE(后来分析应该是死循环了)。。。
瞎搞了一下没什么结果,于是把popoQQQ的std搬了下来,又手写了个暴力输出决策点,开拍,12:00之前拍出三个WA点,吃完饭发现又拍出错来了,于是回去睡午觉。
下午14:00又开始拍,发现了一个致命的错误,应该把根节点s与点p斜率和p与p的前驱(or后继)的斜率比较,我把s和s的前驱(or后继)直接拿去比了,这时15:00。
然后发现拍大一点都可以过,但是小数据如果两个点横坐标相同,求斜率会挂掉(popoQQQ的std好像挂了?),于是求斜率的地方写了个特判,拍了几十秒没挂,交一发500msWA,此时15:40。
把特判去掉,果真RE。然后又发现横坐标相同splay中顺序又有可能会挂,于是又在splay里写了个特判,这回拍了好久都没挂了,此时16:00。
自信交一发,0msWA!!!气啊,于是开始颓了。。。颓到16:50,想起同学们都还在物理周练,又不好意思在颓了,问bzoj的admin要了一发数据,
一测,AC了!!!
满怀着好奇又交了一发,竟然OJ上也A了,看一眼之前0msWA的程序,没有删文件输入输出,BZOJ竟然报WA不报RE,妈的。。。于是一天就毁在一题上。。。

题解:HINT里面都说了,买进就要全买进,卖出就要全卖出,于是dp方程就很好想了,f[i]表示第i天卖出所有AB券总共能获得的钱,于是dp[i]=max{a[i] *x[j]+b[i] *y[j]} (1<=j<=i-1) ,a[],b[]表示当天标价,x[],y[]表示把那天的钱分别能换成多少AB券,其实这里枚举的j就是那天买进。引用popoQQQ的一句话“万事具备,只是AB不单调”。。。于是必须要用splay来维护决策的凸壳。
我是这样实现的,按X坐标顺序建splay,每个点记两个斜率qk和hk表示这个点和前一个点/后一个点的斜率(第一个点qk=0,最后一个点hk=-INF)。插入一个点的时候,先把它扔进splay里旋到根s,然后在它左子树中找p.qk>k(p,s)的最小的p点,旋到左子树根,删除它的右子树,若找不到大于的,整个左子树都应该删掉。右子树同理。找最优决策点的时候(加入查找斜率为k),应该在splay中找那个qk>=k>=hk的点。
听说还有CDQ分治做法。

代码:
这种题pascal狗能200行左右已经不错了。。。

type
  tree=^treenode;
  treenode=record
    x,y,qk,hk:double;
    l,r,f:tree;
  end;
const maxl=1000000000;
  eps=0.0000000001;
var
  n,i,j,size:longint;
  s,f:double;
  a,b,r,x,y:array[0..100100]of double;
  str,dc:tree;
function max(x,y:double):double;
begin
  if x>y then exit(x)
  else exit(y);
end;
function calk(x,y:tree):double;
begin
  if abs(x^.x-y^.x)<eps then
  begin
    if (x^.y-y^.y)*(x^.x-y^.x)>0 then exit(maxl)
    else exit(-maxl);
  end;
  exit((x^.y-y^.y)/(x^.x-y^.x));
end;
procedure left(x:tree);
begin
  x^.f^.r:=x^.l;
  if x^.l<>nil then x^.l^.f:=x^.f;
  x^.l:=x^.f;
  x^.f:=x^.l^.f;
  x^.l^.f:=x;
  if x^.f<>nil then
  begin
    if x^.f^.l=x^.l then x^.f^.l:=x
    else x^.f^.r:=x;
  end;
end;
procedure right(x:tree);
begin
  x^.f^.l:=x^.r;
  if x^.r<>nil then x^.r^.f:=x^.f;
  x^.r:=x^.f;
  x^.f:=x^.r^.f;
  x^.r^.f:=x;
  if x^.f<>nil then
  begin
    if x^.f^.l=x^.r then x^.f^.l:=x
    else x^.f^.r:=x;
  end;
end;
procedure splay(var s:tree;x:tree);
var
  p,sf:tree;
begin
  sf:=s^.f;
  while x^.f<>sf do
  begin
    p:=x^.f;
    if p^.f=sf then
    begin
      if p^.l=x then right(x)
      else left(x);
    end
    else
    begin
      if p^.f^.l=p then
      begin
        if p^.l=x then right(p)
        else left(x);
        right(x);
      end
      else
      begin
        if p^.r=x then left(p)
        else right(x);
        left(x);
      end;
    end;
  end;
  s:=x;
end;

procedure ins(var s:tree;x,y:double);
var
  p,last,qz,hz:tree;
begin
  if s=nil then
  begin
    new(s);
    s^.x:=x;s^.y:=y;s^.qk:=0;s^.hk:=-maxl;
    s^.l:=nil;s^.r:=nil;s^.f:=nil;
    exit;
  end;
  p:=s;
  while p<>nil do
  begin
    last:=p;
    if (x<p^.x)or((abs(x-p^.x)<eps)and(y>p^.y)) then p:=p^.l
    else p:=p^.r;
  end;

  new(p);
  p^.x:=x;
  p^.y:=y;
  p^.qk:=0; p^.hk:=-maxl;
  p^.l:=nil; p^.r:=nil;
  if (x<last^.x)or((abs(x-last^.x)<eps)and(y>last^.y)) then last^.l:=p
  else last^.r:=p;
  p^.f:=last;
  splay(s,p);

end;
procedure inspoint(var s:tree;x,y:double);
var
  p,q,last:tree;
begin
  ins(s,x,y);
  last:=s;
  p:=s^.l;
  while p<>nil do
  begin
    if (abs(s^.x-p^.x)>eps)and(calk(s,p)+eps<p^.qk) then begin last:=p; p:=p^.r end
    else  p:=p^.l;
  end;
  if s^.l<>nil then
  begin
    if last=s then
    begin
      s^.l^.f:=nil;
      s^.l:=nil;
      s^.qk:=0
    end
    else
    begin
      splay(s^.l,last);
      if last^.r<>nil then last^.r^.f:=nil;
      last^.r:=nil;
      s^.qk:=calk(s,last);
      last^.hk:=s^.qk;
    end;
  end;

  last:=s;
  p:=s^.r;
  while p<>nil do
  begin
    if (abs(s^.x-p^.x)>eps)and(calk(s,p)+eps>p^.hk) then begin last:=p; p:=p^.l; end
    else p:=p^.r;
  end;
  if s^.r<>nil then
  begin
    if last=s then
    begin
      s^.r^.f:=nil;
      s^.r:=nil;
      s^.hk:=-maxl;
    end
    else
    begin
      splay(s^.r,last);
      if last^.l<>nil then last^.l^.f:=nil;
      last^.l:=nil;
      s^.hk:=calk(s,last);
      last^.qk:=s^.hk;
    end;
  end;
end;

function find(var s:tree;k:double):tree;
var
  p:tree;
begin
  p:=s;
  while p<>nil do
  begin
    if (p^.qk>=k)and(p^.hk<=k) then exit(p);
    if p^.hk>k then p:=p^.r
    else p:=p^.l
  end;
end;
begin
  readln(n,s);
  for i:=1 to n do
    readln(a[i],b[i],r[i]);
  f:=s;
  str:=nil;
  for i:=1 to n do
  begin
    if i<>1 then
    begin
      dc:=find(str,-a[i]/b[i]);
      f:=max(f,a[i]*dc^.x+b[i]*dc^.y);
    end;
    x[i]:=f/(a[i]+b[i]/r[i]);
    y[i]:=f/(a[i]*r[i]+b[i]);
    inspoint(str,x[i],y[i]);
  end;
  writeln(f:0:3);
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值