货币兑换 [Noi2007 ,Codevs 1797]

69 篇文章 0 订阅
1 篇文章 0 订阅

题目地址请点击——


货币兑换


这里写图片描述


Solution

f(x) 表示第 x 天最多获得的金钱,则方程为:

f(1)=s,f(i)=max(maxi1j=1f(j)riai+biajrj+bj,f(i1))

移项,化简得到斜率公式:

a(i)b(i)[A(j)r(j)A(k)r(k)]>[A(k)A(j)]

其中 A(x)=f(x)/(ajrj+bj)
可以用斜率优化,但因为 a(i)b(i) A(i)r(i) 均不单调,所以要用cdq分治


Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

#define Max(x,y) ((x)>(y)?(x):(y))
#define eps (1e-8)
#define MAXN 100010
#define INF 1e+10

using namespace std;

struct Day{double a,b,r,x,y,A;int num;}day[MAXN],t[MAXN];
struct KKK{double ks;int num;}stack[MAXN];
double f[MAXN];
int n,s,Max_Right,limit=sizeof(Day),Lastcost;

inline int compare(double x){
    if(fabs(x)<=eps)return 0;
    return x>0?1:-1;
}

bool cmp(Day x,Day y){
    if(fabs(x.a/x.b-y.a/y.b)<=eps||x.a/x.b-y.a/y.b<0)return true;
    return false;
}

double get_k(int x,int y){
    if(day[x].x==day[y].x)return INF;
    return (day[x].y-day[y].y)/(day[x].x-day[y].x);
}

void solve(int l,int r){
    if(l==r){
        f[day[l].num]=Max(f[day[l].num],f[day[l].num-1]);
        day[l].A=f[day[l].num]/(day[l].a*day[l].r+day[l].b);
        day[l].x=day[l].A*day[l].r,day[l].y=-day[l].A;
    }
    else{
        int mid=(l+r)>>1,fir=l,sec=mid+1;
        for(int i=l;i<=r;i++){
            if(day[i].num<=mid)t[fir++]=day[i];
            else t[sec++]=day[i];
        }
        for(int i=l;i<=r;i++)day[i]=t[i];
        solve(l,mid);Max_Right=Lastcost=1;
        stack[Max_Right].num=l;stack[Max_Right].ks=-INF;
        for(int i=l+1;i<=mid;i++){
            while(compare(get_k(stack[Max_Right].num,i)-stack[Max_Right].ks)<=0)Max_Right--;
            stack[++Max_Right].num=i;stack[Max_Right].ks=get_k(stack[Max_Right-1].num,i);
        }
        stack[Max_Right+1].ks=INF;
        for(int i=mid+1;i<=r;i++){
            double tmp=day[i].a/day[i].b;
            while((compare(stack[Lastcost].ks-tmp)>0||compare(stack[Lastcost+1].ks-tmp)<0)&&Lastcost<Max_Right)Lastcost++;
            int pos=day[stack[Lastcost].num].num,real=day[i].num;
            f[real]=Max(f[real],f[pos]*(day[stack[Lastcost].num].r*day[i].a+day[i].b)/(day[stack[Lastcost].num].a*day[stack[Lastcost].num].r+day[stack[Lastcost].num].b));
        }
        solve(mid+1,r);
        fir=l,sec=mid+1;
        for(int i=l;i<=r;i++){
            if(fir>mid)t[i]=day[sec++];
            else if(sec>r)t[i]=day[fir++];
            else{
                double tx=day[fir].x,ty=day[sec].x;
                if(compare(tx-ty)<=0)t[i]=day[fir++];
                else t[i]=day[sec++];
            }
        }
        for(int i=l;i<=r;i++)day[i]=t[i];
    }
}

int main(){

    freopen("cash.in","r",stdin);
    freopen("cash.out","w",stdout);

    scanf("%d%d",&n,&s);
    for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&day[i].a,&day[i].b,&day[i].r),day[i].num=i;
    sort(day+1,day+n+1,cmp);
    f[1]=s;solve(1,n);
    printf("%.3lf",f[n]);

    return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值