Processing math: 100%

[NOI2007]货币兑换 cdq分治,斜率优化

[NOI2007]货币兑换

LG传送门

妥妥的nlogncdq做法。

这题用cdq分治也可以nlogn但是在洛谷上竟然比一些优秀的splay跑得慢真是见了鬼了看来还是人丑常数大的问题

先推式子#

(这一段与其他题解不会有太多不同,已经了解了的同学可以略过,注意一下转移中xk表示什么就行了。)

f[i]表示到第i天最多有多少钱,g[i]表示用第i天时的钱最多能买多少B券,易知g[i]=f[i]r[i]a[i]+b[i]

得到转移:f[i]=max{i1maxj=1{g[j]b[i]a[i]+r[j]g[j]}a[i],f[i1]},外面的max可以单独判,里面的max可以看出是一个斜率优化的式子(把b[i]a[i]视作x,把g[j]视作k,把r[j]g[j]视作b)。但是我们发现斜率k并不是单调的,所以传统的斜率优化就无法解决这个问题了。

这时就衍生出两种写法了,一种是用splay维护凸包,一种是用cdq分治处理转移,我们要介绍的是后者。

考虑分治#

对于任意一个f[i],我们只要考虑到所有1ji1对它的影响就行了,cdq分治擅长处理这类问题。

对于一段区间[l,r],先递归左子区间[l,m],保证[l,m]fg值都已经得到了;把左子区间按k递增排序,把右子区间按x递增排序,就可以按平时的斜率优化来O(n)转移;再把右子区间按在原序列中的位置递增排序,然后递归右子区间,此时左子区间对右子区间的影响都已经被考虑完了;边界是l==r,到这里我们可以发现1i1i的影响都已经被考虑过了,别忘了f[i1]f[i]的转移。

这样做是O(n(logn)2)的,让人有点不爽,事实上我们可以做到O(nlogn)

怎样做到1个log#

事实上cdq分治本身是一个归并的过程,我们可以利用这个过程去掉排序的复杂度。

我们希望拿到[l,r]这个区间的时候x是单调的,于是在外面把原序列按x递增排序;拿到一个x递增的区间后,我们希望在原序列中靠左的东西去到左子区间,于是我们把[l,r]扫一遍,把在原序列中位置m(m=l+r>>1)的东西放左边,m的放右边,而且左右子区间对于x的单调性没有受到影响;我们要处理左边对右边的影响,于是先递归左子区间,再像平时一样斜率优化处理转移,然后递归右子区间;我们希望一个区间的左子区间递归回来的时候是对于k单调递增的,于是在最后对k做一遍归并排序。这样每一层递归是O(n)的。

奉上蒟蒻的大常数代码。

Copy
//written by newbiechd #include <iostream> #include <iomanip> #include <algorithm> #define R register #define I inline #define D double using namespace std; const int N = 100003; const D eps = 1e-8; int q[N]; D f[N], g[N]; struct cash { int id; D a, b, r, x; cash() {} cash(int id, D a, D b, D r) : id(id), a(a), b(b), r(r), x(b / a) {} I int operator < (cash q) { return x != q.x ? x < q.x : id < q.id; } }p[N], b[N]; I D cross(int u, int v) { return (p[u].r * g[p[u].id] - p[v].r * g[p[v].id]) / (g[p[v].id] - g[p[u].id]); } I D calc(int u, int v) { return g[p[u].id] * (p[v].x + p[u].r); } I void update(int u, D v) { if (f[p[u].id] < v) f[p[u].id] = v, g[p[u].id] = f[p[u].id] / (p[u].b + p[u].r * p[u].a); } void solve(int l, int r) { if (l == r) { update(l, f[p[l].id - 1]); return ; } R int m = (l + r) >> 1, i, h, t; for (h = l, t = m + 1, i = l; i <= r; ++i) p[i].id <= m ? b[h++] = p[i] : b[t++] = p[i]; for (i = l; i <= r; ++i) p[i] = b[i]; solve(l, m), h = 1, t = 0; for (i = l; i <= m; ++i) { while (h < t && cross(q[t], i) < cross(q[t - 1], i) + eps) --t; q[++t] = i; } for (; i <= r; ++i) { while (h < t && calc(q[h], i) < calc(q[h + 1], i) + eps) ++h; update(i, calc(q[h], i) * p[i].a); } solve(m + 1, r); for (h = l, t = m + 1, i = l; h <= m && t <= r; ) g[p[h].id] < g[p[t].id] ? b[i++] = p[h++] : b[i++] = p[t++]; while (h <= m) b[i++] = p[h++]; while (t <= r) b[i++] = p[t++]; for (i = l; i <= r; ++i) p[i] = b[i]; } int main() { ios::sync_with_stdio(0); R int n, i; D a, b, r; cin >> n >> f[1]; for (i = 1; i <= n; ++i) cin >> a >> b >> r, p[i] = cash(i, a, b, r); g[1] = f[1] / (p[1].r * p[1].a + p[1].b), sort(p + 1, p + n + 1), solve(1, n), cout << fixed << setprecision(3) << f[n]; return 0; }
posted @   newbiechd  阅读(356)  评论(0编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· 程序员常用高效实用工具推荐,办公效率提升利器!
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 【译】WinForms:分析一下(我用 Visual Basic 写的)
点击右上角即可分享
微信分享提示
CONTENTS