BJOI2017 喷式水战改
题目链接。
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 喷式水战改的更多相关文章
- [bzoj4906][BeiJing2017]喷式水战改
来自FallDream的博客,未经允许,请勿转载,谢谢. [题目背景] 拿到了飞机的驾照(?),这样补给就不愁了 XXXX年XX月XX日 拿到了喷气机(??)的驾照,这样就飞得更快了 XXXX年XX月 ...
- [BJOI2017]魔法咒语 --- AC自动机 + 矩阵优化
bzoj 4860 LOJ2180 洛谷P3175 [BJOI2017]魔法咒语 题目描述: Chandra 是一个魔法天才. 从一岁时接受火之教会洗礼之后,Chandra 就显示出对火元素无 ...
- 6.在MVC中使用泛型仓储模式和依赖注入实现增删查改
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pat ...
- Linux.NET实战手记—自己动手改泥鳅(上)
各位读者大家好,不知各位读者有否阅读在下的前一个系列<Linux.NET 学习手记>,在前一个系列中,我们从Linux中Mono的编译安装开始,到Jexus服务器的介绍,以及如何在Linu ...
- Linux.NET实战手记—自己动手改泥鳅(下)
在上回合中,我们不痛不痒的把小泥鳅的数据库从只能供在Windows下运行的Access数据库改为支持跨平台的MYSQL数据库,毫无营养的修改,本回合中,我们将把我们修改后得来的项目往Linux中部署. ...
- Android 打开方式选定后默认了改不回来?解决方法(三星s7为例)
Android 打开方式选定后默认了改不回来?解决方法(三星s7为例) 刚刚在测试东西,打开一个gif图,然后我故意选择用支付宝打开,然后...支付宝当然不支持,我觉得第二次打开它应该还会问我,没想到 ...
- 把PDF的底色改成护眼色,这样读起文章来就不是很累了······
PDF格式背景改变方法如下: 打开PDF 点击 编辑 ->首选项->辅助工具->选中"替换文档颜色"和" 自定义颜色"->将背景颜色改成 ...
- 3.EF 6.0 Code-First实现增删查改
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-entity-framework-5-0-code- ...
- 4.在MVC中使用仓储模式进行增删查改
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-using-the-repository-pattern-in-mvc/ 系列目录: ...
随机推荐
- (2)ElasticSearch在linux环境中集成IK分词器
1.简介 ElasticSearch默认自带的分词器,是标准分词器,对英文分词比较友好,但是对中文,只能把汉字一个个拆分.而elasticsearch-analysis-ik分词器能针对中文词项颗粒度 ...
- python之 栈与队列
忍不住想报一句粗口"卧槽"这尼玛python的数据结构也太特么方便了吧 想到当初学c语言的数据结构的时候,真的是一笔一划都要自己写出来,这python尼玛直接一个模块就ok 真的是 ...
- linux开机启动及运行级别、root密码丢失、单用户模式只读的处理方法
linux系统启动大致步骤如下: 加电自检-->根据BIOS中的设置从指定的设备启动-->找到设备MBR中的bootloader引导启动系统-->启动kernel-->启动i ...
- 关闭防火墙和设置主机名和ip及克隆机网卡处理方法
关闭防火墙: service NetworkManager stop --图形化用ifconfig之前先关掉网络服务. chkconfig NetworkManager off (将开机自启动关掉,使 ...
- Ceph根据Crush位置读取数据
前言 在ceph研发群里面看到一个cepher在问关于怎么读取ceph的副本的问题,这个功能应该在2012年的时候,我们公司的研发就修改了代码去实现这个功能,只是当时的硬件条件所限,以及本身的稳定性问 ...
- Flink处理函数实战之三:KeyedProcessFunction类
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- EOF和scanf函数
EOF和scanf函数 scanf函数的返回值 scanf函数返回成功读入的数据项数,读入数据时遇到了"文件结束(end of file)"或者错误则返回EOF,EOF定义为int ...
- phpmyadmin反序列化漏洞(WooYun-2016-199433)
简介 环境复现:https://github.com/vulhub/vulhub 线上平台:榆林学院内可使用协会内部的网络安全实验平台 phpMyAdmin是一套开源的.基于Web的MySQL数据库管 ...
- bWAPP----HTML Injection - Reflected (URL)
HTML Injection - Reflected (URL) 核心代码 1 <div id="main"> 2 3 <h1>HTML Injection ...
- redis的分布式锁工具LockUtil
/** * 基于redis的分布式锁工具 * * @author yuyufeng * */ public class LockUtil { // 获取redis static JedisPool j ...