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


      先说一下斜率优化:这是一种经典的dp优化,是OI中利用数形结合的思想解决问题的典范,通常用于优化dp,有时候其他的一些决策优化也会用到,看待他的角度一般有两种,但均将决策看为二维坐标系上的点,并转化为维护凸壳,一种根据两点的斜率与某一常数的大小关系推断二者的优劣,一种将转移方程化为相关直线方程,通过取得最大(小)截距来求最优解。关于其实现方法上,当点的x坐标单调时,可依据比较常数是否单调选择单调队列或单调栈,而当其x坐标不单调时常常使用CDQ分治或平衡树来实现。

      千万别用替罪羊来写动态凸壳!!!

      用平衡树来写动态凸壳的话,很容易想到的是维护凸壳点集并使x坐标单调,那么这个时候你不仅得到了单调的x坐标,点与点之间的斜率也就是单调的了,这个时候你就可以给每个点再维护两个值:他与他在凸壳上左边的点连成的线段的斜率和他与他在凸壳上右边的点连成的线段的斜率(边界设为正无穷和负无穷),维护了这两个值你就可以直接在二叉树上查找最优决策点,而不用二分。当插入一个点的时候,你可以在这个点两边暴力pop,这样均摊nlogn,也可以直接在这个点两边进行二叉查找这样严格nlogn,但是常数相对较小。用splay或者Treap(无旋有旋都可以,只是常数差异)会很优秀,但是用替罪羊的话,呵呵.......不仅码量爆炸,而且各种操作都不是很好实现,就拿维护上面说的两个值来讲,如果你的替罪羊删除用的是标记,那么你就要**了.....因为废点在二叉查找的时候也需要有指示作用,但是你不能把他放入凸壳来维护,所以你还要维护一下盖住他的线段的斜率......(可能写替罪羊的时候把维护的信息统一改为前驱实点和后继实点会好很多)

      下面是巨丑巨慢的替罪羊程序.....

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ft first
    #define sd second
    #define mmp(a,b) (std::make_pair(a,b))
    #define get_k(x,y) (((x)->b-(y)->b)/((x)->a-(y)->a))
    typedef double db;
    typedef std::pair<db,db> pdd;
    const int N=100010;
    const db eps=1e-8;
    const db Inf=1./0.;
    const db oo=-1./0.;
    db A[N],B[N],R[N],f[N];
    int n,s;
    namespace SGT{
      const db alpha=0.75;
      struct ScapeGoat_Tree{
        ScapeGoat_Tree *ch[2],*zz;
        int size,cover,ex;
        db a,b,lk,rk;
        inline void pushup(){
          size=ch[0]->size+ch[1]->size+ex;
          cover=ch[0]->cover+ch[1]->cover+1;
        }
        inline void update();
        inline bool isbad(){
          return cover*alpha+5<ch[0]->cover||cover*alpha+5<ch[1]->cover;
        }
      }*root,*null,node[N],*list[N];
      int len,sz;
      inline void ScapeGoat_Tree:: update(){
          if(ch[0]->zz!=null)return void(zz=ch[0]->zz);
          if(ex)return void(zz=this);
          zz=ch[1]->zz;
        }
      inline void Init(){
        null=node+(sz++);
        null->ch[0]=null->ch[1]=null->zz=null;
        root=null;
      }
      inline void travel(ScapeGoat_Tree *p){
        if(p==null)return;
        travel(p->ch[0]);
        if(p->ex)list[++len]=p;
        travel(p->ch[1]);
      }  
      inline ScapeGoat_Tree *divide(int l,int r){
        if(l>r)return null;
        int mid=(l+r)>>1;
        list[mid]->ch[0]=divide(l,mid-1);
        list[mid]->ch[1]=divide(mid+1,r);
        list[mid]->pushup();
        list[mid]->update();
        return list[mid];
      }
      inline void rebuild(ScapeGoat_Tree *&p){
        len=0,travel(p),p=divide(1,len);
      }
      inline int get_rank(db x){
        int ret=0;
        ScapeGoat_Tree *p=root;
        while(p!=null)
          if(p->a<x+eps)
            ret+=p->ch[0]->size+p->ex,p=p->ch[1];
          else
            p=p->ch[0];
        return ret;
      }
      inline ScapeGoat_Tree *get_kth(int k){
        ScapeGoat_Tree *p=root;
        while(true)
          if(p->ex&&p->ch[0]->size+p->ex==k)
            return p;
          else if(p->ch[0]->size>=k)
            p=p->ch[0];
          else k-=p->ch[0]->size+p->ex,p=p->ch[1];
      }
      inline ScapeGoat_Tree **insert(ScapeGoat_Tree *&p,db x,db y){
        if(p==null){
          p=node+(sz++);
          p->ch[0]=p->ch[1]=null;
          p->size=p->cover=p->ex=1;
          p->a=x,p->b=y,p->zz=p;
          return &null;
        }
        ++p->size,++p->cover;
        ScapeGoat_Tree **ret=insert(p->ch[p->a<x],x,y);
        if(p->isbad())ret=&p;
        p->update();
        return ret;
      }
      inline void Insert(db x,db y){
        int k=get_rank(x);
        ScapeGoat_Tree *p1,*p2,*p3;
        ScapeGoat_Tree **p=insert(root,x,y);
        p2=node+(sz-1);
        if(k!=0){
          p1=get_kth(k);
          p1->rk=p2->lk=get_k(p1,p2);
        }else
          p2->lk=Inf;
        if(k+2<=root->size){
          p3=get_kth(k+2);
          p2->rk=p3->lk=get_k(p2,p3);
        }else
          p2->rk=oo;
        if(*p!=null)rebuild(*p);
      }
      inline void del(ScapeGoat_Tree *p,int k){
        --p->size;
        if(p->ex&&p->ch[0]->size+p->ex==k){
          p->ex=0,p->update();
          return;
        }
        if(p->ch[0]->size>=k)del(p->ch[0],k);
        else del(p->ch[1],k-p->ch[0]->size-p->ex);
        p->update();
      }
      inline void Del(ScapeGoat_Tree *p){
        int k=get_rank(p->a);
        del(root,k);
        ScapeGoat_Tree *p1,*p2;
        if(root->size!=0){
          if(k==1){
            p2=get_kth(k);
            p2->lk=Inf;
          }else if(k>root->size){
            p1=get_kth(k-1);
            p1->rk=oo;
          }else{
            p2=get_kth(k);
            p1=get_kth(k-1);
            p1->rk=p2->lk=get_k(p1,p2);
          }
        }
        if(root->size+5<root->cover*alpha)rebuild(root);
      }
      inline void Del(int k){
        del(root,k);
        ScapeGoat_Tree *p1,*p2;
        if(root->size!=0){
          if(k==1){
            p2=get_kth(k);
            p2->lk=Inf;
          }else if(k>root->size){
            p1=get_kth(k-1);
            p1->rk=oo;
          }else{
            p2=get_kth(k);
            p1=get_kth(k-1);
            p1->rk=p2->lk=get_k(p1,p2);
          }
        }
        if(root->size+5<root->cover*alpha)rebuild(root);
      }
      inline bool die(int k){
        ScapeGoat_Tree *p=get_kth(k);
        return p->lk<p->rk+eps;
      }
      inline void Ins(db x,db y){
        int k=get_rank(x);
        if(k==0){
          Insert(x,y),++k;
        }else{
          ScapeGoat_Tree *p=get_kth(k);
          if(fabs(p->a-x)<eps)
            p->b=std::max(p->b,y);
          else
            Insert(x,y),++k;
        }
        if(die(k))return void(Del(k));
        while(k!=root->size&&die(k+1))Del(k+1);
        while(k!=1&&die(k-1))Del(k-1),--k;
      }
      inline pdd query(ScapeGoat_Tree *p,db k){
        if(p->ex&&k<=p->lk+eps&&eps+k>=p->rk)
          return mmp(p->a,p->b);
        if((p->ex&&k>p->lk+eps)||(p->ex==0&&(p->ch[1]->size==0||p->ch[1]->zz->lk<k)))
          return query(p->ch[0],k);
        else
          return query(p->ch[1],k);
      }
    }
    int main(){
      scanf("%d%d",&n,&s);
      int i;SGT::Init();
      for(i=1;i<=n;++i)
        scanf("%lf%lf%lf",&A[i],&B[i],&R[i]);
      f[1]=s;
      db y=s/(R[1]*A[1]+B[1]),x=y*R[1];
      SGT::Insert(x,y);
      pdd ret;
      for(i=2;i<=n;++i){
        ret=SGT::query(SGT::root,-A[i]/B[i]);
        f[i]=B[i]*ret.sd+A[i]*ret.ft;
        f[i]=std::max(f[i],f[i-1]);
        y=f[i]/(R[i]*A[i]+B[i]),x=y*R[i];
        SGT::Ins(x,y);
      }
      printf("%.3f",f[n]);
      return 0;
    }
  • 相关阅读:
    组合算法问题
    递归之全排列问题
    递归之整数划分问题
    利用Python完成一个小游戏:随机挑选一个单词,并对其进行乱序,玩家要猜出原始单词
    对数组元素进行排序的方法总结(利用C++)
    用c++语言编写函数 int index(char *s,char * t),返回字符串t在字符串s中出现的最左边的位置,如果s中没有与t匹配的子串,则返回-1。类似于索引的功能。
    用MFC完成一个简单的猜数字游戏: 输入的四位数中,位置和数字都正确为A,数字相同而位置不同的为B。
    用Matlab完成:从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。
    利用matlab实现以下功能:将一个正整数分解质因数。例如:输入90,打印出90=2*3*3*5。
    白书_倒三角形_C语言描述
  • 原文地址:https://www.cnblogs.com/TSHugh/p/8168851.html
Copyright © 2020-2023  润新知