@description@

Picks博士观察完金星凌日后,设计了一个复杂的电阻器。为了简化题目,题目中的常数与现实世界有所不同。

这个电阻器内有编号为 1∼n 的 n 个独立水箱,水箱呈圆柱形,底面积为 1 \(m^2\),每个水箱在顶部和底部各有一个阀门,可以让水以 1 \(m^3/s\) 的流量通过,每个水箱的上阀门接水龙头,可以无限供应水,下阀门不接东西,可以让水流出。水箱顶部和底部都有一个接口,水的电阻率为 1 Ω⋅m。

水箱的高度足够高,有一个导电浮标浮在水面上,通过导线与水箱顶的接口相连。一开始时第 i 个水箱中有 \(a_i\) \(m^3\) 的水。

Picks博士接下来就需要对这个复杂的电阻器进行调试。他会进行以下五种操作。

1、打开编号在 [l,r] 中的所有水箱的上方阀门 x 秒,然后关上它们的上方阀门。

2、打开编号在 [l,r] 中的所有水箱的下方阀门 x 秒,然后关上它们的下方阀门。

3、将编号在 [l,r] 中的所有水箱的下方阀门与大海通过连通器以一定方式相连,使得这些水箱中都恰拥有 \(x\) \(m^3\) 的水,然后关上它们的下方阀门,撤去连通器。

4、在第 y 个水箱的上下方接口处接上一个电动势为 1 V 的电源,电源没有内阻,Picks博士会测量出通过电源的电流大小,之后撤去该电源。

5、由于水浸泡过的地方会留下明显的水渍而没有被水浸泡过的地方不会有,Picks博士可以据此测量出此时第 y 个水箱的水渍高度,以推断曾经最多有多少水,节约他的建造成本。

现在,他请你来帮他做预实验,你能告诉他每次测量得到的电流大小以及测量得到的最多的水量是多少吗?

原题传送门。

@solution@

来翻译一遍题目的操作:

t = 1 : [li, ri] 中的所有 aj 变成 aj + xi。

t = 2 : [li, ri] 中的所有 aj 变成 max(aj - xi, 0)。

t = 3 : [li, ri] 中的所有 aj 变成 xi。

t = 4 : 询问 ayi。

t = 5 : 询问 ayi 在历史上的最大值。

可以发现 1 操作也可写成 max(aj + xi, 0),3 操作也可写成 max(-∞, xi),这样所有修改的形式就统一成 max(x + a, b) 了。

两个修改标记的复合为 max(max(x + a1, b1) + a2, b2) = max(x + max(a1, a2), max(b1 + a2, b2))。

注意到这个复合运算满足结合律(但不满足交换律),所以可以用线段树维护。

如何维护历史最大值?我们尝试对两个修改标记取 max:max(max(x + a1, b1), max(x + a2, b2)) = max(x + max(a1, a2), max(b1, b2)),发现是可行的。

于是我们再在每个结点处维护一个历史最大未下传标记即可。

@accepted code@

#include <cstdio>
#include <algorithm>
using namespace std; typedef long long ll; const int MAXN = 500000;
const ll INF = 1E16; ll add(ll x, ll y) {
return x + y > INF ? INF : (x + y < -INF ? -INF : x + y);
}
struct tag{
ll a, b; tag() {}
tag(ll _a, ll _b) : a(_a), b(_b) {}
friend tag add(tag a, tag b) {
return tag(add(a.a, b.a), max(add(a.b, b.a), b.b));
}// max(max(x + a.a, a.b) + b.a, b.b)
friend tag max(tag a, tag b) {
return tag(max(a.a, b.a), max(a.b, b.b));
}// max(max(x + a.a, a.b), max(x + b.a, b.b))
}; int a[MAXN + 5];
struct segtree{
#define lch (x << 1)
#define rch (x << 1 | 1) int le[4*MAXN + 5], ri[4*MAXN + 5];
tag tg[4*MAXN + 5], mt[4*MAXN + 5];
void build(int x, int l, int r) {
le[x] = l, ri[x] = r, tg[x] = tag(0, 0);
if( l == r ) {
tg[x] = mt[x] = tag(a[l], 0);
return ;
}
int m = (l + r) >> 1;
build(lch, l, m), build(rch, m + 1, r);
}
void maintain(int x, tag nwt, tag mxt) {
mt[x] = max(mt[x], add(tg[x], mxt));
tg[x] = add(tg[x], nwt);
}
void pushdown(int x) {
if( tg[x].a || tg[x].b ) {
maintain(lch, tg[x], mt[x]);
maintain(rch, tg[x], mt[x]);
tg[x] = mt[x] = tag(0, 0);
}
}
void modify(int x, int l, int r, tag t) {
if( l > ri[x] || r < le[x] )
return ;
if( l <= le[x] && ri[x] <= r ) {
maintain(x, t, t);
return ;
}
pushdown(x);
modify(lch, l, r, t), modify(rch, l, r, t);
}
tag query(int x, int p, int type) {
if( le[x] == ri[x] ) return (type == 4 ? tg[x] : mt[x]);
int m = (le[x] + ri[x]) >> 1;
pushdown(x);
if( p <= m ) return query(lch, p, type);
else return query(rch, p, type);
}
}T; int main() {
int n, m; scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++) scanf("%d", &a[i]);
T.build(1, 1, n);
for(int i=1;i<=m;i++) {
int t; scanf("%d", &t);
if( t <= 3 ) {
int l, r, k; tag p; scanf("%d%d%d", &l, &r, &k);
if( t == 1 ) p = tag(k, 0);
if( t == 2 ) p = tag(-k, 0);
if( t == 3 ) p = tag(-INF, k);
T.modify(1, l, r, p);
}
else {
int y; scanf("%d", &y);
tag p = T.query(1, y, t);
printf("%lld\n", max(p.a, p.b));
}
}
} /*
t = 1 : [li, ri] + x
t = 2 : max([li, ri] - x, 0)
t = 3 : [li, ri] = x
t = 4 : query ax
t = 5 : query history max{ax}
*/

@details@

听说是吉如一线段树(segment tree beats)的一部分来着,不过不是很清楚。

@uoj - 164@ 【清华集训2015】V的更多相关文章

  1. UOJ #164 [清华集训2015]V (线段树)

    题目链接 http://uoj.ac/problem/164 题解 神仙线段树题. 首先赋值操作可以等价于减掉正无穷再加上\(x\). 假设某个位置从前到后的操作序列是: \(x_1,x_2,..., ...

  2. 清华集训2015 V

    #164. [清华集训2015]V http://uoj.ac/problem/164 统计 描述 提交 自定义测试 Picks博士观察完金星凌日后,设计了一个复杂的电阻器.为了简化题目,题目中的常数 ...

  3. 【uoj#164】[清华集训2015]V 线段树维护历史最值

    题目描述 给你一个长度为 $n$ 的序列,支持五种操作: $1\ l\ r\ x$ :将 $[l,r]$ 内的数加上 $x$ :$2\ l\ r\ x$ :将 $[l,r]$ 内的数减去 $x$ ,并 ...

  4. 「清华集训2015」V

    「清华集训2015」V 题目大意: 你有一个序列,你需要支持区间加一个数并对 \(0\) 取 \(\max\),区间赋值,查询单点的值以及单点历史最大值. 解题思路: 观察发现,每一种修改操作都可以用 ...

  5. [UOJ#274][清华集训2016]温暖会指引我们前行

    [UOJ#274][清华集训2016]温暖会指引我们前行 试题描述 寒冬又一次肆虐了北国大地 无情的北风穿透了人们御寒的衣物 可怜虫们在冬夜中发出无助的哀嚎 “冻死宝宝了!” 这时 远处的天边出现了一 ...

  6. UOJ #164. 【清华集训2015】V | 线段树

    题目链接 UOJ #164 题解 首先,这道题有三种询问:区间加.区间减(减完对\(0\)取\(\max\)).区间修改. 可以用一种标记来表示--标记\((a, b)\)表示把原来的值加上\(a\) ...

  7. UOJ#164:【清华集训2015】V

    浅谈区间最值操作与历史最值问题:https://www.cnblogs.com/AKMer/p/10225100.html 题目传送门:http://uoj.ac/problem/164 论文题.论文 ...

  8. 2018.07.28 uoj#164. 【清华集训2015】V(线段树)

    传送门 线段树好题. 要求支持的操作: 1.区间变成max(xi−a,0)" role="presentation" style="position: rela ...

  9. UOJ #164 【清华集训2015】 V

    题目链接:V 这道题由于是单点询问,所以异常好写. 注意到每种修改操作都可以用一个标记\((a,b)\)表示.标记\((a,b)\)的意义就是\(x= \max\{x+a,b\}\) 同时这种标记也是 ...

随机推荐

  1. python3.x 基础八:socket网络编程

    Socket socket就是一直以来说的“套接字”,用于描述:ip:端口,是通信链的句柄,客户端通过这个句柄进行请求和响应 普通文件的操作顺序:打开-读写-关闭,针对的是文件 socket是特殊的文 ...

  2. Form action 方法上传文件

    <form method="post" id="form1" runat="server" enctype="multipa ...

  3. Puppeteer笔记(七):Puppeteer切换浏览器TAB页

    一.Puppeteer切换浏览器TAB页 1.browser.pages() 二.上手实例Demo 功能测试:打开www.ly.com首页,定位搜索"苏州",获取新打开页面上的搜索 ...

  4. 王玉兰201771010128《面向对象与程序设计(Java)》第十一周学习总结

    一:理论知识部分: (1)集合:集合(Collection或称为容器)是一种包含多个元素并提供对所包含元素操作方法的类,其包含的元素可以由同一类型的对象组成,也可以由不同类型的对象组成. A:集合类的 ...

  5. MySQL(7)— 索引

    七.索引 MySQL官方对索引的定义为:索引(Index)是帮助 MySQL 高效 获取数据的数据结构. 7-1.索引的分类 主键索引 (primary key) 唯一的标识,主键不可重复,只能有一个 ...

  6. AVL树的创建--C语言实现

    AVL树是一种自平衡(Self-balancing)二叉查找树(Binary Search Tree),要求任何一个节点的左子树和右子树的高度之差不能超过1. AVL树的插入操作首先会按照普通二叉查找 ...

  7. Intel FPGA Clock Region概念以及用法

    目录 Intel FPGA 的Clock Region概念 Intel 不同系列FPGA 的Clock Region 1. Clock Region Assignments in Intel Stra ...

  8. ORCLE 列转行

    字符串转多列 实际上就是拆分字符串的问题,可以使用 substr.instr.regexp_substr函数方式 字符串转多行 使用union all函数等方式 wm_concat函数 wm_conc ...

  9. 监控-zabbix

    1:什么是监控? 监控:安防的监控  看监控,事后追责 linux监控: 事前预警,数据分析 2:常见的linux监控命令 cpu             1 top 2 htop 3 uptime ...

  10. 读Pyqt4教程,带你入门Pyqt4 _008

    QCalendarWidget QCalendarWidget 提供基于月份的日历窗口组件,它允许用户简单并且直观的选择日期. #!/usr/bin/python # -*- coding: utf- ...