P4027 [NOI2007] 货币兑换
[NOI2007] 货币兑换
题目描述
小 Y 最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A 纪念券(以下简称 A 券)和 B 纪念券(以下简称 B 券)。每个持有金券的顾客都有一个自己的帐户。金券的数目可以是一个实数。
每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第
为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。
比例交易法分为两个方面:
a) 卖出金券:顾客提供一个
b) 买入金券:顾客支付
例如,假定接下来
时间 | |||
---|---|---|---|
第一天 | |||
第二天 | |||
第三天 |
假定在第一天时,用户手中有
用户可以执行以下的操作:
时间 | 用户操作 | 人民币(元) | A 券的数量 | B 券的数量 |
---|---|---|---|---|
开户 | 无 | |||
第一天 | 买入 |
|||
第二天 | 卖出 |
|||
第二天 | 买入 |
|||
第三天 | 卖出 |
注意到,同一天内可以进行多次操作。
小 Y 是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经知道了未来
输入格式
第一行两个正整数
接下来
输出格式
只有一个实数
样例 #1
样例输入 #1
3 100
1 1 1
1 2 2
2 2 3
样例输出 #1
225.000
提示
时间 | 用户操作 | 人民币(元) | A 券的数量 | B 券的数量 |
---|---|---|---|---|
开户 | 无 | |||
第一天 | 买入 |
|||
第二天 | 卖出 |
|||
第二天 | 买入 |
|||
第三天 | 卖出 |
本题没有部分分,你的程序的输出只有和标准答案相差不超过
测试数据设计使得精度误差不会超过
对于
对于
对于
对于
输入文件可能很大,请采用快速的读入方式。
必然存在一种最优的买卖方案满足:
每次买进操作使用完所有的人民币,每次卖出操作卖出所有的金券。
Solution
假设一天的钱是
那么有:
这样就有了一个
若
这样就可以斜率优化了。但是非常恶心的是
一种可行的解决方式是用 CDQ 分治进行转移,先按照
- 若当前区间长度为
,尝试从 转移。 - 递归处理左半区间。
- 将左半区间加入单调队列,维护凸包。
- 计算左半区间对右半区间的贡献。
- 递归处理右半区间。
- 将当前区间按照
进行归并排序,方便之后维护单调队列。
这样这道题就解决了。代码上需要注意一些细节。如果开始对
完整代码
#include<bits/stdc++.h>
#undef DEBUG
using namespace std;
namespace Hanx16qwq {
constexpr int _SIZE = 1e5;
constexpr long double eps = 1e-18;
int n;
long double S;
struct Node {
int id;
long double x, y;
}q[_SIZE + 5], ql[_SIZE + 5], qr[_SIZE + 5];
long double A[_SIZE + 5], B[_SIZE + 5], R[_SIZE + 5];
long double f[_SIZE + 5];
int s[_SIZE + 5], head, tail;
long double px(int x) {return -A[x] / B[x];}
long double slope(int j, int i) {
return (q[i].y - q[j].y) / (q[i].x - q[j].x + eps);
}
void CDQ(int l, int r) {
if (l == r) {
f[l] = max(f[l], f[l - 1]);
q[l].x = f[l] * R[l] / (A[l] * R[l] + B[l]);
q[l].y = f[l] / (A[l] * R[l] + B[l]);
return;
}
int mid = (l + r) >> 1, lp = 0, rp = 0;
for (int i = l; i <= r; i++)
if (q[i].id <= mid) ql[++lp] = q[i];
else qr[++rp] = q[i];
for (int i = 1; i <= lp; i++) q[i + l - 1] = ql[i];
for (int i = 1; i <= rp; i++) q[mid + i] = qr[i];
CDQ(l, mid);
head = 1, tail = 0;
for (int i = l; i <= mid; i++) {
while (tail > head && slope(s[tail], i) > slope(s[tail - 1], s[tail])) tail--;
s[++tail] = i;
}
for (int I = mid + 1; I <= r; I++) {
int i = q[I].id;
while (tail > head && slope(s[head], s[head + 1]) > -(A[i] / B[i])) head++;
int j = s[head];
f[i] = max(f[i], q[j].x * A[i] + q[j].y * B[i]);
}
CDQ(mid + 1, r);
lp = l, rp = mid + 1; int p = 0;
while (lp <= mid && rp <= r)
if (q[lp].x < q[rp].x) ql[++p] = q[lp++];
else ql[++p] = q[rp++];
while (lp <= mid) ql[++p] = q[lp++];
while (rp <= r) ql[++p] = q[rp++];
for (int i = 1; i <= p; i++) q[l + i - 1] = ql[i];
}
void main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> S;
for (int i = 1; i <= n; i++) {
cin >> A[i] >> B[i] >> R[i];
q[i].id = i;
q[i].x = R[i] * S / (A[i] * R[i] + B[i]);
q[i].y = S / (A[i] * R[i] + B[i]);
f[i] = S;
}
sort(q + 1, q + n + 1, [&](Node x, Node y) {
return px(x.id) > px(y.id);
});
CDQ(1, n);
cout << fixed << setprecision(3) << f[n] << '\n';
}
}
signed main() {
#ifdef DEBUG
freopen("../test.in", "r", stdin);
freopen("../test.out", "w", stdout);
#endif
Hanx16qwq::main();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步