[bzoj1492]NOI2007 CASH (DP+CDQ/Splay)

来源:互联网 发布:spss mac使用教程 编辑:程序博客网 时间:2024/06/07 02:01

题目传送门

我们可以很快发现一个dp解法:可以发现答案一定是由所持有的卷的数目计算出来的,而卷的数目是可以转移的,所以令f[i]表示在第i天可以得到的B卷的数目的最大值,可以得到转移方程为:f[i]=max{ans,Rate[j]f[j]a[i]+f[j]b[i]}Rate,ans=max{ans,Rate[j]f[j]a[i]+f[j]b[i]}

分析对于i的两个决策j和k,可以发现决策j比决策k优当且仅当:
f[j]Rate[j]f[k]Rate[k]f[j]f[k]>b[i]a[i]时,决策j比k优。
我们定义slope(j,k)表示这个式子的左侧,G[i]=f[i]Rate[i]
即,slope(j,k)>b[i]a[i],在二维平面上定义点Xi=(fi,Gi),那么式子左侧就是个两个点之间的斜率。
我们就可以斜率优化啦:
1) 给一个点集中添加一个点。
2) 给定一个负数斜率K,询问所有斜率为K且过点集中任意点的直线在y轴上的截距的最大值。

dp的第二种解法:
我们令f[i]表示最优答案, x[i],y[i]分别表示两种卷在i时刻的最大数量。
于是f[i]=max{f[i1],x[j]a[i]+y[j]b[i]}
接着我们考虑如何斜率优化,y[j]=(a[i]b[i]x[j]+f[i]b[i]),我们把a[i]b[i]看做斜率,观察发现,对于平面上的点(x,y)来说,要使得f[i]最大,我们需要使得直线在y轴上的截距最大。
于是我们维护平面上的点的上凸包。
解法1:Splay
用splay把所有点维护起来,每个点记录和左右两边的点的斜率。
对于每次的询问,我们用一条斜率为a[i]b[i]的直线去切割这个凸壳,就可以找到最优的转移点。
对于每次插入点,首先取出x[j],然后向两边找到它能作为凸包时需要连接的点,删去中间经过的点。若找不到,也就说明它在凸包内,是一个凹点,把它自己删除。
//注意下面代码为了写起来方便x,y与上面的方程解释是反的
//坑填好啦
//你问我CDQ在哪里? 当然是选择坑着啦

#include<bits/stdc++.h>using namespace std;const int maxn=1e5+10;const double eps=1e-8;const double inf=1e20;double X[maxn],Y[maxn];//splayint fa[maxn],ch[maxn][2];double lk[maxn],rk[maxn];int root,sz;//splayinline bool get(int x){    return (ch[fa[x]][1]==x);}inline void rotate(int x){    int old=fa[x];int oldf=fa[old];int d=get(x);    fa[ch[x][d^1]]=old;ch[old][d]=ch[x][d^1];    fa[old]=x;ch[x][d^1]=old;    fa[x]=oldf;    if(oldf)        ch[oldf][ch[oldf][1]==old]=x;}inline void splay(int x,int g=0){    for(int y;(y=fa[x])!=g;rotate(x)){        if(fa[y]!=g)            rotate((get(x)==get(y))? y:x);    }    if(!g)        root=x;}inline double getk(int j,int k){    if(fabs(X[j]-X[k]<=eps)) return inf;    return (Y[j]-Y[k])/(X[j]-X[k]);}inline int get_pre(){    int p=ch[root][0],ret=p;    while(p){        if(getk(root,p)+eps>=lk[p])            p=ch[p][0];        else            ret=p,p=ch[p][1];    }    return ret;}inline int get_suc(){    int p=ch[root][1],ret=p;    while(p){        if(getk(p,root)<=rk[p]+eps)            p=ch[p][1];        else             ret=p,p=ch[p][0];    }    return ret;}inline void insert(int &r,int pre,int p){    if(!r){        r=p;        fa[p]=pre;        return;    }    if(X[p]<=X[r]+eps) insert(ch[r][0],r,p);    else insert(ch[r][1],r,p);}inline void update(int p){    splay(p);    if(ch[p][0]){        int l=get_pre();        splay(l,p);        ch[l][1]=0;        lk[p]=rk[l]=getk(p,l);     }else lk[p]=inf;    if(ch[p][1]){        int r=get_suc();        splay(r,p);        ch[r][0]=0;        rk[p]=lk[r]=getk(r,p);    }else rk[p]=-inf;    //删除凹点    if(lk[p]<rk[p]+eps){        root=ch[p][0];        ch[root][1]=ch[p][1];        fa[ch[p][1]]=root;        fa[root]=0;        rk[root]=lk[ch[p][1]]=getk(ch[root][1],root);    }}inline int getpos(double k){    int p=root;    while(p){        //切割到了凸包         if(lk[p]+eps>=k&&k+eps>=rk[p])            break;        if(lk[p]<k+eps)            p=ch[p][0];        else p=ch[p][1];    }    return p;}inline double getans(double a,double b){    int p=getpos(-b/a);    return Y[p]*a+X[p]*b;}double a,b,rate;int n;double ans;int main(int argc,const char * argv[]){    scanf("%d%lf",&n,&ans);    for(int i=1;i<=n;i++){        scanf("%lf%lf%lf",&a,&b,&rate);        ans=max(ans,getans(a,b));        X[i]=ans/(a*rate+b);        Y[i]=X[i]*rate;        insert(root,0,i);//将X,Y所代表的点插入平衡树        update(i);    }    printf("%.3lf",ans);    return 0;}
阅读全文
1 0
原创粉丝点击