[NOI2007]货币兑换
题目描述
小 Y 最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A 纪念券(以下简称 A 券)和 B 纪念券(以下简称 B 券)。每个持有金券的顾客都有一个自己的帐户。金券的数目可以是一个实数。
每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第 K 天中 A 券和 B 券的价值分别为 AKA_KAK 和BKB_KBK (元/单位金券)。
为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。
比例交易法分为两个方面:
a) 卖出金券:顾客提供一个[0,100]内的实数 OP 作为卖出比例,其意义为:将 OP%的 A 券和 OP%的 B 券以当时的价值兑换为人民币;
b) 买入金券:顾客支付 IP 元人民币,交易所将会兑换给用户总价值为IP 的金券,并且,满足提供给顾客的 A 券和 B 券的比例在第 K 天恰好为 RateKRate_KRateK ;
例如,假定接下来 3 天内的 AkA_kAk 、BkB_kBk 、RateKRate_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_KAK 、BKB_KBK 、RateKRate_KRateK ,意义如题目中所述。
输出格式:只有一个实数 MaxProfit,表示第 N 天的操作结束时能够获得的最大的金钱数目。答案保留 3 位小数。
输入输出样例
说明
时间 用户操作 人民币(元) 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}10−7 。
对于 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 }