题目链接

Description

维护一个序列,支持操作:

  • 每次在 \(P_i\) 位置后插入一段 \(X_i\) 单位的燃料,这一段有三个模式,对应的能量分别是 \(A_i, B_i, C_i\)。然后将这个序列分成四段(一段可以为空),权值分别是 \(ABCA\),最后求最大总能量。

Solution

首先我们发现一个性质,就是说一段其实在最优解下的状态是相同的,否则可以把状态价值高的蔓延到低的,会更优。

如果不考虑查询,可以把每一段看做一个大小为 \(X_i\) 的点,这个插入操作在时间复杂度能接受的范围内其实是一个平衡树的操作。因为每次插入最坏情况下会分裂一个点,所以点数最多 \(2n\)。我们可以考虑是否能在维护平衡树的时候同步维护答案。

最大总能量显然是 DP,而这道题的 DP 可以写出线性 DP和区间 DP 两种,考虑如果插入一个元素,如果是线性 \(DP\) ,这个元素后面的所有都要重新算一遍,复杂度爆炸。而区间 DP 能够满足我们的要求的。

因为平衡树满足 BST 的性质,所以每个节点的子树可以看做一段区间,每次修改,可以修改的过程同时维护每个节点所在子树区间的答案即可。

状态设计

设 \(f_{i,j}\) 为一个节点所在的子树所形成的区间,状态区间是 \([i, j]\) 所搞成的最大总能量。

初始状态

考虑每个点初始的答案。

$f_{i, j} = X_i \times $ \([i, j]\) 状态中最大的单位权值。

状态转移

考虑一段区间的合并,设左边的为 \(A.f\),右边的是 \(B.f\),答案是 \(C.f\)

有 \(C.f_{i, j} = \max(A.f_{i, k} + B.f_{k, j} )\) 。

在真正实现的时候,先让 $A = $ 左儿子, $B = $当前节点,合并后再合并右儿子即可,合并顺序不影响答案。

时间复杂度

因为每次合并的时候复杂度\(O(4 ^ 3)\),所以总复杂度 \(O(64NlogN)\)

Code

实现下来用的是 Fhq-Treap

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std; const int N = 1e5 + 10; typedef long long LL; int n, idx, rt;
LL last = 0; struct F{
LL w[4][4];
F(){}
F (int a, int b, int c, int v) {
memset(w, 0, sizeof w);
w[0][0] = w[3][3] = (LL)a * v, w[1][1] = (LL)b * v, w[2][2] = (LL)c * v;
for (int i = 0; i < 4; i++)
for (int j = i + 1; j < 4; j++) w[i][j] = max(w[i][j - 1], w[j][j]);
}
F operator + (const F &b) const {
F c; memset(c.w, 0, sizeof c.w);
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
for (int k = i; k <= j; k++) c.w[i][j] = max(c.w[i][j], w[i][k] + b.w[k][j]);
return c;
}
} val[N << 2], sum[N << 2]; struct T{
int l, r, rnd, sz, len, a, b, c;
LL tot;
} t[N << 2]; int getNode(int a, int b, int c, int len) {
t[++idx] = (T) { 0, 0, rand(), 1, len, a, b, c, len};
val[idx] = sum[idx] = F(a, b, c, len);
return idx;
} void pushup(int p) {
t[p].sz = t[t[p].l].sz + t[t[p].r].sz + 1;
t[p].tot = t[t[p].l].tot + t[t[p].r].tot + t[p].len;
sum[p] = val[p];
if (t[p].l) sum[p] = sum[t[p].l] + sum[p];
if (t[p].r) sum[p] = sum[p] + sum[t[p].r];
} int merge(int A, int B) {
if (!A || !B) return A + B;
if (t[A].rnd < t[B].rnd) {
t[A].r = merge(t[A].r, B);
pushup(A);
return A;
} else {
t[B].l = merge(A, t[B].l);
pushup(B);
return B;
}
} // 按 tot 的 size 分裂,让 x 的 tot 总和 <= k
void split1(int p, LL k, int &x, int &y) {
if (!p) { x = y = 0; return; }
if (t[t[p].l].tot + t[p].len <= k) {
x = p;
split1(t[p].r, k - (t[t[p].l].tot + t[p].len), t[p].r, y);
} else {
y = p;
split1(t[p].l, k, x, t[p].l);
}
pushup(p);
} // 按 size 分裂,让 x 的 sz 总和 <= k
void split2(int p, int k, int &x, int &y) {
if (!p) { x = y = 0; return; }
if (t[t[p].l].sz + 1 <= k) {
x = p;
split2(t[p].r, k - (t[t[p].l].sz + 1), t[p].r, y);
} else {
y = p;
split2(t[p].l, k, x, t[p].l);
}
pushup(p);
} int main() {
int x, y, z;
scanf("%d", &n);
while (n--) {
LL p; int a, b, c, v; scanf("%lld%d%d%d%d", &p, &a, &b, &c, &v);
split1(rt, p, x, y); split2(y, 1, y, z);
int w = getNode(a, b, c, v), l = p - t[x].tot;
if (l) t[w].l = getNode(t[y].a, t[y].b, t[y].c, l);
if (t[y].len - l) t[w].r = getNode(t[y].a, t[y].b, t[y].c, t[y].len - l);
pushup(w);
rt = merge(x, merge(w, z));
printf("%lld\n", sum[rt].w[0][3] - last);
last = sum[rt].w[0][3];
}
return 0;
}

BJOI2017 喷式水战改的更多相关文章

  1. [bzoj4906][BeiJing2017]喷式水战改

    来自FallDream的博客,未经允许,请勿转载,谢谢. [题目背景] 拿到了飞机的驾照(?),这样补给就不愁了 XXXX年XX月XX日 拿到了喷气机(??)的驾照,这样就飞得更快了 XXXX年XX月 ...

  2. [BJOI2017]魔法咒语 --- AC自动机 + 矩阵优化

    bzoj 4860   LOJ2180   洛谷P3175 [BJOI2017]魔法咒语 题目描述: Chandra 是一个魔法天才. 从一岁时接受火之教会洗礼之后,Chandra 就显示出对火元素无 ...

  3. 6.在MVC中使用泛型仓储模式和依赖注入实现增删查改

    原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pat ...

  4. Linux.NET实战手记—自己动手改泥鳅(上)

    各位读者大家好,不知各位读者有否阅读在下的前一个系列<Linux.NET 学习手记>,在前一个系列中,我们从Linux中Mono的编译安装开始,到Jexus服务器的介绍,以及如何在Linu ...

  5. Linux.NET实战手记—自己动手改泥鳅(下)

    在上回合中,我们不痛不痒的把小泥鳅的数据库从只能供在Windows下运行的Access数据库改为支持跨平台的MYSQL数据库,毫无营养的修改,本回合中,我们将把我们修改后得来的项目往Linux中部署. ...

  6. Android 打开方式选定后默认了改不回来?解决方法(三星s7为例)

    Android 打开方式选定后默认了改不回来?解决方法(三星s7为例) 刚刚在测试东西,打开一个gif图,然后我故意选择用支付宝打开,然后...支付宝当然不支持,我觉得第二次打开它应该还会问我,没想到 ...

  7. 把PDF的底色改成护眼色,这样读起文章来就不是很累了······

    PDF格式背景改变方法如下: 打开PDF 点击 编辑 ->首选项->辅助工具->选中"替换文档颜色"和" 自定义颜色"->将背景颜色改成 ...

  8. 3.EF 6.0 Code-First实现增删查改

    原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-entity-framework-5-0-code- ...

  9. 4.在MVC中使用仓储模式进行增删查改

    原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-using-the-repository-pattern-in-mvc/ 系列目录: ...

随机推荐

  1. shell编程之俄罗斯方块

    按键获取: 向上  ^[[A 向下  ^[[B 向左  ^[[D 向右  ^[[C 其中  ^[为ESC键. 按键获取的具体shell代码如下所示: #! /bin/bash GetKey() { a ...

  2. 创建一个自定义名称的Ceph集群

    前言 这里有个条件,系统环境是Centos 7 ,Ceph 的版本为Jewel版本,因为这个组合下是由systemctl来进行服务控制的,所以需要做稍微的改动即可实现 准备工作 部署mon的时候需要修 ...

  3. FreeMark导出word文件

    1.编辑好word 2.将word模板另存为xml格式, 把需要动态生成的文字用${xxx}代替 eg: 张强 替换为:${name} 注意:图片是很长的一个base64的字符,同样替换就好 比如替换 ...

  4. Spring第三天,详解Bean的生命周期,学会后让面试官无话可说!

    点击下方链接回顾往期 不要再说不会Spring了!Spring第一天,学会进大厂! Spring第二天,你必须知道容器注册组件的几种方式!学废它吊打面试官! 今天讲解Spring中Bean的生命周期. ...

  5. 学习笔记:[算法分析]数据结构与算法Python版

    什么是算法分析 对比程序,还是算法? ❖如何对比两个程序? 看起来不同,但解决同一个问题的程序,哪个" 更好"? ❖程序和算法的区别 算法是对问题解决的分步描述 程序则是采用某种编 ...

  6. Markdown进阶

    ### 事项清单 - [x] 拖地 - [x] 擦窗 - [ ] 写作业 - [ ] 交资料 效果 事项清单 [x] 拖地 [x] 擦窗 [ ] 写作业 [ ] 交资料 流程图 graph LR A[ ...

  7. day97:MoFang:移动端APP开发准备&移动端项目搭建&APICloud前端框架

    目录 1.移动端开发相关概念 1.APP类型 2.移动端屏幕介绍 3.移动端自适配方案 4.元信息(meta) 2.APP开发准备 1.注册APPCLoud账号 2.下载APP开发编辑器 3.下载AP ...

  8. Java基础教程——Lambda表达式

    Lambda表达式 Java8引入Lambda表达式,可以使代码更简洁. 格式:参数,箭头,代码 (参数名)->{代码} Lambda表达式体现了"函数式编程思想"-- 面向 ...

  9. mysql反序索引

    关于mysql的排序 参考:https://www.cnblogs.com/lccsblog/p/12733773.html 转载: https://www.cnblogs.com/lynn919/p ...

  10. 浅谈JAVA servlet

    1.servlet是什么? servlet的本质是接口,接口就是一种规范.我们来看一下servlet接口中都有哪些函数: 图片来源:https://www.cnblogs.com/whgk/p/639 ...