[NOI2007]货币兑换

题目描述

小 Y 最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A 纪念券(以下简称 A 券)和 B 纪念券(以下简称 B 券)。每个持有金券的顾客都有一个自己的帐户。金券的数目可以是一个实数。

每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第 K 天中 A 券和 B 券的价值分别为 AKA_KAKBKB_KBK (元/单位金券)。

为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。

比例交易法分为两个方面:

a) 卖出金券:顾客提供一个[0,100]内的实数 OP 作为卖出比例,其意义为:将 OP%的 A 券和 OP%的 B 券以当时的价值兑换为人民币;

b) 买入金券:顾客支付 IP 元人民币,交易所将会兑换给用户总价值为IP 的金券,并且,满足提供给顾客的 A 券和 B 券的比例在第 K 天恰好为 RateKRate_KRateK

例如,假定接下来 3 天内的 AkA_kAkBkB_kBkRateKRate_KRateK 的变化分别为:

时间 AkA_kAk BkB_kBk RatekRate_kRatek

第一天 1 1 1

第二天 1 2 2

第三天 2 2 3

假定在第一天时,用户手中有 100 元人民币但是没有任何金券。

用户可以执行以下的操作:

时间 用户操作 人民币(元) A 券的数量 B 券的数量

开户 无 100 0 0

第一天 买入 100 元 0 50 50

第二天 卖出 50% 75 25 25

第二天 买入 60 元 15 55 40

第三天 卖出 100% 205 0 0

注意到,同一天内可以进行多次操作。

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

输入输出格式

输入格式:

第一行两个正整数 N、S,分别表示小 Y 能预知的天数以及初始时拥有的钱数。

接下来 N 行,第 K 行三个实数 AKA_KAKBKB_KBKRateKRate_KRateK ,意义如题目中所述。

输出格式:

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

输入输出样例

输入样例#1: 复制
3 100
1 1 1
1 2 2
2 2 3
输出样例#1: 复制
225.000

说明

时间 用户操作 人民币(元) A 券的数量 B 券的数量

开户 无 100 0 0

第一天 买入 100 元 0 50 50

第二天 卖出 100% 150 0 0

第二天 买入 150 元 0 75 37.5

第三天 卖出 100% 225 0 0

本题没有部分分,你的程序的输出只有和标准答案相差不超过0.0010.0010.001 时,才能获得该测试点的满分,否则不得分。

测试数据设计使得精度误差不会超过 10−710^{-7}107 。

对于 40%的测试数据,满足 N ≤ 10;

对于 60%的测试数据,满足 N ≤ 1 000;

对于 100%的测试数据,满足 N ≤ 100 000;

对于 100%的测试数据,满足:

0 < AKA_KAK ≤ 10;

0 < BKB_KBK ≤ 10;

0 < RateKRate_KRateK ≤ 100

MaxProfit ≤ 10910^9109 ;

输入文件可能很大,请采用快速的读入方式。

必然存在一种最优的买卖方案满足:

每次买进操作使用完所有的人民币;

每次卖出操作卖出所有的金券。

令f[i]为当前的收益(如果是金劵则兑换为现金)

因为根据贪心身上不是现金就是金劵

显然$O(n^2)$的DP出来了

$f[i]=max(A_i*x_j+B_i*y_j)$

$x_j$和$y_j$为在j处的f[j]能买的A劵和B劵数

可以算出来

100分要用到斜率优化+CDQ

每一次处理出[l,mid]内部更新的f值

然后用这些更新[mid+1,r]的f值

然后在考虑[mid+1,r]的内部更新

重点是用[l,mid]更新[mid+1,r]

且要在线性时间

将上述式子变为斜距式:

$y=-\frac{A_i}{B_i}x+\frac{f_i}{B_i}$

如果$f_i$要尽可能大,那么截距就大,斜率是固定的$rate_i$

对于[l,mid]中的(x,y)维护一个上凸包,一个斜率为k

显然只要使第$i$天的“这条直线”切于前面[l,mid]天的点对组成的上凸包时即可得到fimax 。

于是我们可以把[mid+1,r]按k排序,于是可以单调栈

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 struct ZYYS
 8 {
 9   int id;
10   double k,x,y;
11 }s[100001];
12 bool cmp1(ZYYS a,ZYYS b)
13 {
14   return a.k>b.k;
15 }
16 bool cmp2(ZYYS a,ZYYS b)
17 {
18   return a.id<b.id;
19 }
20 bool cmp3(ZYYS a,ZYYS b)
21 {
22   return (a.x==b.x)?a.y<b.y:a.x<b.x;
23 }
24 double f[100001],rate[100001],a[100001],b[100001],inf=-1e15,eps=1e-10;
25 int n;
26 double S,ans;
27 double get_k(int j,int i)
28 {
29   if (fabs(s[i].x-s[j].x)<=eps) return -inf;
30   return (s[i].y-s[j].y)/(s[i].x-s[j].x);
31 }
32 void solve(int l,int r)
33 {int i;
34   if (l==r)
35     {
36       f[l]=max(f[l-1],f[l]);
37       s[l].x=(rate[l]*f[l])/(rate[l]*a[l]+b[l]);
38       s[l].y=(f[l])/(rate[l]*a[l]+b[l]);
39       return;
40     }
41   int mid=(l+r)/2;
42   sort(s+l,s+r+1,cmp2);
43   solve(l,mid);
44   int top=0,st[100001];
45   for (i=l;i<=mid;i++)
46     {
47       while (top>1&&get_k(st[top-1],st[top])<get_k(st[top],i)) top--;
48       st[++top]=i;
49     }
50   sort(s+mid+1,s+r+1,cmp1);
51   int loc=1;
52   for (i=mid+1;i<=r;i++)
53     {
54       while (loc<top&&s[i].k<get_k(st[loc],st[loc+1])) loc++;
55       f[s[i].id]=max(f[s[i].id],a[s[i].id]*s[st[loc]].x+b[s[i].id]*s[st[loc]].y);
56     }
57   solve(mid+1,r);
58   sort(s+l,s+r+1,cmp3);
59 }
60 int main()
61 {int i;
62   cin>>n>>S;
63   f[0]=S;
64   for (i=1;i<=n;i++)
65     {
66       scanf("%lf%lf%lf",&a[i],&b[i],&rate[i]);
67       s[i].id=i;s[i].k=-a[i]/b[i];
68     }
69   solve(1,n);
70   for (i=0;i<=n;i++)
71     ans=max(ans,f[i]);
72   printf("%.3lf\n",ans);
73 }

 

posted @ 2018-03-22 22:10  Z-Y-Y-S  阅读(252)  评论(0编辑  收藏  举报