BZOJ 1492: [NOI2007]货币兑换Cash 斜率优化 + splay动态维护凸包

Code: 

#include<bits/stdc++.h>
#define maxn 300000  
#define inf 0x3f3f3f3f
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std;
const double eps = 1e-9;  
int root;  
struct Splaytree
{
    #define get(x) (ch[f[x]][1]==x) 
    int cnt; 
    int f[maxn],ch[maxn][2];
    double X[maxn],Y[maxn],lk[maxn],rk[maxn]; 
    inline void rotate(int x)
    {
        int old=f[x],fold=f[old],which=get(x); 
        ch[old][which]=ch[x][which^1],f[ch[old][which]]=old; 
        ch[x][which^1]=old,f[old]=x,f[x]=fold; 
        if(fold) ch[fold][ch[fold][1]==old]=x;   
    }
    inline void splay(int x,int &tar)
    {
        int fa,u=f[tar]; 
        for(;(fa=f[x])!=u;rotate(x)) 
            if(f[fa]!=u) 
                rotate(get(fa)==get(x)?fa:x); 
        tar=x;  
    }
    inline double slope(int i,int j)
    {
        return fabs(X[i]-X[j])<=eps ? -inf : (Y[i]-Y[j])/(X[i]-X[j]);  
    }
    inline void insert(int &o,double x,double y,int last)
    {
        if(!o) 
        {
            o=++cnt; 
            X[o]=x,Y[o]=y,f[o]=last; 
            return; 
        }
        insert(ch[o][x-X[o]>eps],x,y,o);   
    }
    inline int pre(int x)
    {
        int cur=ch[x][0],re=0; 
        while(cur)
        {
            if(slope(x,cur)+eps>=rk[cur]) re=cur,cur=ch[cur][0]; 
            else cur=ch[cur][1]; 
        }
        return re; 
    }
    inline int nxt(int x)
    {
        int cur=ch[x][1],re=0; 
        while(cur)
        {
            if(slope(x,cur)<=lk[cur]+eps) re=cur,cur=ch[cur][1]; 
            else cur=ch[cur][0];  
        }
        return re; 
    }
    inline int getl(int x)
    {
        while(ch[x][0]) x=ch[x][0]; 
        return x;   
    }
    inline int getr(int x) 
    {
        while(ch[x][1]) x=ch[x][1];
        return x; 
    }
    inline void del(int x)
    { 
        if(!ch[x][0]) 
        {
            int right=getl(ch[x][1]);   
            splay(right,ch[x][1]),root=right; 
            ch[x][1]=f[root]=0;  
            lk[root]=inf;             
        }
        else if(!ch[x][1]) 
        {
            int left=ch[x][0]; 
            splay(left,ch[x][0]),root=left; 
            ch[x][0]=f[root]=0; 
            rk[root]=-inf;   
        }
        else 
        {
            int right=getl(ch[x][1]); 
            int left=getr(ch[x][0]);  
            splay(left,ch[x][0]);   
            splay(right,ch[x][1]); 
            root=left;  
            ch[root][1]=right,f[right]=root; 
            rk[root]=lk[right]=slope(root,right);     
        }
    }
    inline void maintain(int x)
    {
        splay(x,root); 
        if(ch[x][0]) 
        {
            int left=pre(x); 
            if(left) 
            { 
                splay(left,ch[x][0]); 
                ch[left][1]=f[ch[left][1]]=0; 
                rk[left]=lk[x]=slope(left,x); 
            }
            else lk[x]=-inf; 
        }
        else lk[x]=inf; 
        if(ch[x][1]) 
        {
            int right=nxt(x); 
            if(right) 
            {
                splay(right,ch[x][1]);  
                ch[right][0]=f[ch[right][0]]=0; 
                rk[x]=lk[right]=slope(right,x); 
            }
            else rk[x]=inf;      
        }
        else rk[x]=-inf; 
        if(lk[x]-rk[x]<=eps) del(x); 
    }
    inline int getans(int x,double k) 
    {  
    	if(!x) return 0; 
    	if(lk[x]+eps>=k&&rk[x]<=k+eps) return x; 
    	if(lk[x]<k+eps) return getans(ch[x][0],k); 
    	else return getans(ch[x][1],k);   
    }
}splay; 
int n,S; 
double f[maxn],A[maxn],B[maxn],rate[maxn];  
int main()
{
    int i,j; 
    // setIO("input"); 
    scanf("%d%lf",&n,&f[0]);
    for(i=1;i<=n;++i)
    {
        scanf("%lf%lf%lf",&A[i],&B[i],&rate[i]); 
        int j=splay.getans(root,-(A[i]/B[i])); 
        double x=splay.X[j],y=splay.Y[j];  
        f[i]=max(f[i-1],A[i]*x+B[i]*y);     
        y=f[i]/(A[i]*rate[i]+B[i]); 
        x=y*rate[i]; 
        splay.insert(root,x,y,0);          
        splay.maintain(i); 
    } 
    printf("%.3lf",f[n]); 
    return 0; 
}

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值