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 的变化分别为:
假定在第一天时,用户手中有 100元 人民币但是没有任何金券。用户可以执行以下的操作:
注意到,同一天内可以进行多次操作。小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
1 1 1
1 2 2
2 2 3
Sample Output
225.000
HINT
首先贪心的想,每天的操作只有三种可能,什么也不干,全部买入和全部卖出。
设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 ",f[n]); }