【NOI2007】货币兑换


P1472 - 【NOI2007】货币兑换


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


Input

第一行两个正整数N、S,分别表示小Y能预知的天数以及初始时拥有的钱数。接下来N行,第K行三个实数AK、BK、RateK,意义如题目中所述。


Output

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


Sample Input

3 100
1 1 1
1 2 2
2 2 3


Sample Output

225.000


Hint

样例说明:

Pic

数据范围:
测试数据设计使得精度误差不会超过10^-7。
对于40%的测试数据,满足N ≤10;
对于60%的测试数据,满足N ≤1 000;
对于100%的测试数据,满足N ≤100 000;
对于100%的测试数据,满足:
0 < Ak ≤ 10
0 < Bk≤ 10
0 < Ratek ≤ 100
MaxProfit ≤ 10^9
提示
输入文件可能很大,请采用快速的读入方式。
必然存在一种最优的买卖方案满足: 每次买进操作使用完所有的人民币;每次卖出操作卖出所有的金券。
【评分方法】
本题没有部分分,你的程序的输出只有和标准答案相差不超过0.001时,才能获得该测试点的满分,否则不得分。



题目中有一句:
必然存在一种最优的买卖方案满足: 每次买进操作使用完所有的人民币;每次卖出操作卖出所有的金券。
所以可以设Y[i]表示第i天的B能买的B卷数量,则X[i]=Rate[i]
f[i]=A[i]*X[j]+B[i]*Y[j]
所以f[i]=max(f[i],A[i]*(Rate[j]*f[j])/(A[j]*Rate[j]+B[j])
+B[i]*f[j]/(A[j]*Rate[j]+B[j]));
然后就有60分了。
AC做法:斜率优化。
f[i]=A[i]*X[j]+B[i]*Y[j].
f[i]=A[i]*(X[j]+B[i]/A[i]*Y[j])
可以看出A[i]B[i]都是常数。
所以就是求B[i]/A[i]*Y[j]+X[j]的最大值。kx+b,斜率优化。
但这个Y[j]并不是单调的啊,所以单调队列不能用了。
看数据范围,显然这道题的正解并不是O(n)的。
所以就有一种神奇的东西,可以nlogn搞完,叫做CDQ分治...好像这道题就是这个算法的发明题。
按照斜率优化线的做法,要在双端队列中维护斜率递减的直线,好像维护的这东西叫做半平面交。
所以就先按照时间排序,从中间二分,保证左边的时间都小于右边的时间,然后用左边更新右边。
把左边按照斜率升序排序,维护一个半平面交。
把右边按照x坐标升序排序,然后再线性扫一遍,因为这时已经满足单调性了。
还有一堆的细节要处理。
 1     #include <algorithm>
 2     #include <iostream>
 3     #include <cstdlib>
 4     #include <cstring>
 5     #include <cstdio>
 6     #include <cmath>
 7     #define maxn 100010
 8     using namespace std;
 9     double A[maxn],B[maxn],Rate[maxn],f[maxn];
10     struct data{
11       double x,k,b;
12       int id;
13     }g[maxn],p[maxn],q[maxn];
14     bool cmp(const data &a,const data &b){
15       return a.x<b.x;
16     }
17     bool cmpp(const data &a,const data &b){
18       return a.k<b.k;
19     }
20     void CDQ(int l,int r){
21       if(l==r) return;
22       int mid=(l+r)>>1,l1=l,l2=mid+1,head=1,tail=0;
23       double MAX=0;
24       for(int i=l;i<=r;i++){
25         if(g[i].id<=mid) p[l1++]=g[i];
26         else p[l2++]=g[i];
27       }
28       for(int i=l;i<=r;i++) g[i]=p[i];
29       CDQ(l,mid);
30       sort(g+l,g+mid+1,cmpp);
31       for(int i=l;i<=mid;i++){
32         while(head<tail && (g[i].b-q[tail].b)*(-q[tail].k+q[tail-1].k)<=(q[tail].b-q[tail-1].b)*(-g[i].k+q[tail].k))
33           tail--;
34         q[++tail]=g[i],MAX=max(MAX,f[g[i].id]);
35       }
36       sort(g+mid+1,g+r+1,cmp);
37       for(int i=mid+1;i<=r;i++){
38         while(head<tail&&q[head].k*g[i].x+q[head].b<=q[head+1].k*g[i].x+q[head+1].b)
39           head++;
40         f[g[i].id]=max(f[g[i].id],max(MAX,A[g[i].id]*(q[head].k*g[i].x+q[head].b)));
41         g[i].k=f[g[i].id]/(A[g[i].id]*Rate[g[i].id]+B[g[i].id]),g[i].b=g[i].k*Rate[g[i].id];
42       }
43       CDQ(mid+1,r);
44     }
45     int main()
46     {
47       //freopen("!.in","r",stdin);
48       //freopen("!.out","w",stdout);
49       int n;
50       scanf("%d%lf",&n,&f[0]);
51       for(int i=1;i<=n;i++){
52         scanf("%lf%lf%lf",&A[i],&B[i],&Rate[i]);
53         f[i]=f[0],g[i].id=i,g[i].x=B[i]/A[i];
54         g[i].k=f[i]/(A[i]*Rate[i]+B[i]);
55         g[i].b=Rate[i]*f[i]/(A[i]*Rate[i]+B[i]);
56       }
57       CDQ(1,n);
58       /*for(int i=2;i<=n;i++){
59         for(int j=1;j<i;j++)
60         f[i]=max(f[i],A[i]*(Rate[j]*f[j])/(A[j]*Rate[j]+B[j])+B[i]*f[j]/(A[j]*Rate[j]+B[j]));
61         f[i]=max(f[i],f[i-1]);
62       }*/
63       printf("%.3lf",f[n]);
64       return 0;
65     }



原文地址:https://www.cnblogs.com/pantakill/p/6708427.html