BZOJ_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 的变化分别为:
BZOJ_1492_[NOI2007]货币兑换Cash_CDQ分治+斜率优化-LMLPHP
假定在第一天时,用户手中有 100元 人民币但是没有任何金券。用户可以执行以下的操作:
BZOJ_1492_[NOI2007]货币兑换Cash_CDQ分治+斜率优化-LMLPHP
注意到,同一天内可以进行多次操作。小Y是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经
知道了未来N天内的A券和B券的价值以及Rate。他还希望能够计算出来,如果开始时拥有S元钱,那么N天后最多能
够获得多少元钱。

Input

输入第一行两个正整数N、S,分别表示小Y能预知的天数以及初始时拥有的钱数。接下来N行,第K行三个实数AK、B
K、RateK,意义如题目中所述。对于100%的测试数据,满足:0<AK≤10;0<BK≤10;0<RateK≤100;MaxProfit≤1
0^9。
【提示】
1.输入文件可能很大,请采用快速的读入方式。
2.必然存在一种最优的买卖方案满足:
每次买进操作使用完所有的人民币;
每次卖出操作卖出所有的金券。

Output

只有一个实数MaxProfit,表示第N天的操作结束时能够获得的最大的金钱数目。答案保留3位小数。

Sample Input

3 100
1 1 1
1 2 2
2 2 3

Sample Output

225.000

HINT

BZOJ_1492_[NOI2007]货币兑换Cash_CDQ分治+斜率优化-LMLPHP


首先贪心的想,每天的操作只有三种可能,什么也不干,全部买入和全部卖出。

设x[i]为第i天能获得A卷的数量,y[i]为第i天能获得B卷的数量。

y[i]=f[i]/(rate[i]*A[i]+B[i]),x[i]=y[i]*rate[i]。

那么f[i]=max(f[i-1],x[j]*A[i]+y[j]*B[i]),其中我们需要找到一个已经确定F值得j来转移这个过程。

F[i]=X[j]*A[i]+Y[j]*B[i]

F[i]/A[i]=Y[j]*(B[i]/A[i])+X[j]。

把Y[j]当做斜率。那么我们会发现不仅Y[j]不单调,每次切的横坐标(B[i]/A[i])也不单调。

这时候我们使用CDQ分治。

本题的思想:对左边进行处理,处理左边对右边的影响(F的转移),再对右边进行处理。

先按(B[i]/A[i])排序,然后每步再按斜率归并上去。

也就是说我们到了一个局面,满足左边的斜率单调,右边每次切的横坐标单调。

这个就很好办了,对左边单调栈求凸包,然后维护一个指针扫一遍凸包。

正确性:所有能更新F[i]的F[j]在更新F[i]之前都已经更新完毕了,同时F[i]被1~i-1中的每个F[j]都更新到了。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
typedef double f2;
#define N 100050
#define eps 1e-6
f2 xx[N],yy[N],f[N],A[N],B[N],rate[N],K[N],pos[N];
int n,t[N],tmp[N],S[N];
#define Y(i,j) (K[j]*pos[i]+xx[j])
bool cmp(int x,int y) {return pos[x]<pos[y];}
bool judge(int p1,int p2,int p3) {
return (yy[p2]-yy[p3])*(xx[p1]-xx[p2])<=(yy[p1]-yy[p2])*(xx[p2]-xx[p3]);
}
void solve(int l,int r) {
if(l==r) {
f[l]=max(f[l],f[l-1]);
yy[l]=f[l]/(rate[l]*A[l]+B[l]);
xx[l]=yy[l]*rate[l];
K[l]=yy[l];
return ;
}
int mid=(l+r)>>1;
int i,j=l,k=mid+1;
for(i=l;i<=r;i++) {
if(t[i]<=mid) tmp[j++]=t[i];
else tmp[k++]=t[i];
}
for(i=l;i<=r;i++) t[i]=tmp[i];
solve(l,mid);
int top=0;
for(i=l;i<=mid;i++) {
while(top>1&&judge(S[top-1],S[top],t[i])) top--;
S[++top]=t[i];
}
for(j=1,i=mid+1;i<=r;i++) {
while(j<top&&Y(t[i],S[j+1])>=Y(t[i],S[j])) j++;
f[t[i]]=max(f[t[i]],Y(t[i],S[j])*A[t[i]]);
}
solve(mid+1,r);
i=j=l,k=mid+1;
while(j<=mid&&k<=r) {
if(K[t[j]]<=K[t[k]]) tmp[i++]=t[j++];
else tmp[i++]=t[k++];
}
while(j<=mid) tmp[i++]=t[j++];
while(k<=r) tmp[i++]=t[k++];
for(i=l;i<=r;i++) t[i]=tmp[i];
}
int main() {
scanf("%d%lf",&n,&f[0]);
int i,j;
for(i=1;i<=n;i++) {
scanf("%lf%lf%lf",&A[i],&B[i],&rate[i]);
t[i]=i; pos[i]=B[i]/A[i];
}
sort(t+1,t+n+1,cmp);
solve(1,n);
printf("%.3f\n",f[n]);
}
05-08 08:12