1492: [NOI2007]货币兑换Cash 斜率优化 CDQ分治维护凸壳

Description
小Y最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下简称B券)。每个持有金券的顾客都有一个自己的帐户。金券的数目可以是一个实数。每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第 K 天中 A券 和 B券 的价值分别为 AK 和 BK(元/单位金券)。为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。比例交易法分为两个方面:(a)卖出金券:顾客提供一个 [0,100] 内的实数 OP 作为卖出比例,其意义为:将 OP% 的 A券和 OP% 的 B券 以当时的价值兑换为人民币;(b)买入金券:顾客支付 IP 元人民币,交易所将会兑换给用户总价值为 IP 的金券,并且,满足提供给顾客的A券和B券的比例在第 K 天恰好为 RateK;例如,假定接
下来 3 天内的 Ak、Bk、RateK 的变化分别为:

假定在第一天时,用户手中有 100元 人民币但是没有任何金券。用户可以执行以下的操作:

注意到,同一天内可以进行多次操作。小Y是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经知道了未来N天内的A券和B券的价值以及Rate。他还希望能够计算出来,如果开始时拥S元钱,那么N天后最多能够获得多少元钱。

题解:

点这里这篇博客写得挺详细的,那么我就简单总结一下如何用CDQ分治维护凸包:
1、首先按照斜率从大到小排序。
2、对于每个 [ l , r ] 区间,先递归处理 [ l , m i d ] ,此时这些点已经按照x坐标排好序了,然后用这些左边的点构出凸包,这个可以用单调队列维护。
3、处理左边点对右边点的贡献,处理时可以直接删除队头,因为右边的斜率也是单调的。
4、递归处理右边的 [ m i d + 1 , r ] 区间。
最后注意对x相同的点的处理。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=100010;
const int inf=2147483647;
const double eps=1e-8;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
double x[Maxn],y[Maxn];
double A[Maxn],B[Maxn],Rate[Maxn];
bool cmpk(int a,int b)
{
    return (-A[a])/(B[a])>(-A[b])/(B[b]);
}
int n,s,tmp[Maxn],t[Maxn],q[Maxn];
double f[Maxn];
double slope(int a,int b){return (y[a]-y[b])/(x[a]-x[b]);}
void solve(int l,int r)
{
    if(l==r)
    {
        y[t[l]]=f[t[l]]/(Rate[t[l]]*A[t[l]]+B[t[l]]);
        x[t[l]]=Rate[t[l]]*y[t[l]];
        return;
    }
    int mid=l+r>>1,ll=l-1,rr=mid;
    for(int i=l;i<=r;i++)
    if(t[i]<=mid)tmp[++ll]=t[i];
    else tmp[++rr]=t[i];
    for(int i=l;i<=r;i++)t[i]=tmp[i];
    solve(l,mid);
    double mx=0;
    int head=1,tail=0;
    for(int i=l;i<=mid;i++)
    {
        if(head<=tail&&abs(x[t[i]]-x[q[tail]])<=eps)
        {if(y[t[i]]>y[q[tail]])tail--;else continue;}
        while(head<tail&&slope(t[i],q[tail])>=slope(q[tail],q[tail-1]))tail--;
        q[++tail]=t[i];mx=max(mx,f[t[i]]);
    }
    for(int i=mid+1;i<=r;i++)
    {
        while(head<tail&&slope(q[head],q[head+1])>(double)(-A[t[i]])/(double)B[t[i]])head++;
        int p=q[head];
        f[t[i]]=max(f[t[i]],mx);
        f[t[i]]=max(f[t[i]],x[p]*(double)A[t[i]]+y[p]*(double)B[t[i]]);
    }
    solve(mid+1,r);ll=l,rr=mid+1;
    for(int i=l;i<=r;i++)
    {
        if(rr>r||(ll<=mid&&x[t[ll]]<x[t[rr]]))tmp[i]=t[ll++];
        else tmp[i]=t[rr++];
    }
    for(int i=l;i<=r;i++)t[i]=tmp[i];
}
int main()
{
    n=read(),s=read();f[1]=(double)s;
    for(int i=1;i<=n;i++)
    scanf("%lf%lf%lf",&A[i],&B[i],&Rate[i]),t[i]=i;
    sort(t+1,t+1+n,cmpk);
    solve(1,n);
    printf("%.3lf",f[n]);
}

猜你喜欢

转载自blog.csdn.net/baidu_36797646/article/details/79918196