题解 CF739C Alyona and towers
题意
给定一个序列,支持以下操作:
-
区间加
-
整体查询最长的单峰序列(一部分严格单调递增,另一部分严格单调递减)。
思路
线段树的简单题,做法和思路基本和 P4513 差不多,难点在于怎么上传整合各子区间的信息。
为方便处理左右两个子区间的边界问题,维护区间内以下 9 个量:
-
左端点权值
。 -
右端点权值
。 -
从左端点开始的最长下降序列
。 -
从右端点结束的最长上升序列
。 -
从左端点开始的最长的单峰序列
。 -
从右端点结束的最长的单峰序列
。 -
区间内最长的单峰序列
。 -
区间加的懒标记
。 -
区间长度
。
懒标记的下传很常规,修改
重点是上传的部分。
定义
分类讨论:
-
对于
和 直接继承左右区间即可。 -
对于
,分两类讨论:- 若
且 ,说明下降序列一直延续到了右区间, 。 - 若
或 , 。
- 若
-
对于
,分两类讨论:- 若
且 ,说明上升序列一直延续到了左区间, 。 - 若
或 , 。
- 若
-
对于
,若 ,说明左区间是一个上升序列,否则直接继承左子树即可,分五类讨论:- 若
,此时答案延续不到右区间, 。 - 若
且 ,说明左区间的上升序列在右区间还可延续, 。 - 若
且 ,说明左区间的上升序列在右区间不可延续,右区间只可下降, 。 - 若
且 ,说明左区间可能可以令右区间下降增加长度, 。 - 其余情况说明左区间无法连接到右区间,
。
- 若
-
也类似的处理。 -
对于
,有三种继承的情况:- 从
和 继承来, 。 - 若
,此时最大值在左区间, 。 - 若
,此时最大值在右区间, 。
- 从
其他操作就和普通线段树一样了。
本题还有一个稍微简单些的做法,维护一个差分数组,每次整体查询由相邻的连续正数段和连续负数段构成的最长子段,这样每次修改就变成单点加,不需要区间加的懒标记了,维护这个问题的操作和朴素的线段树差不多。
时间复杂度为线段树的
这个数据范围要开 long long
。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3e5 + 10;
int n, m;
ll a[N];
struct Segment {
int l, r, len;
int inc, dec, lans, rans, ans;
ll tag, lv, rv;
} t[N << 2];
#define ls x << 1
#define rs x << 1 | 1
void pushup(int x) {
t[x].lv = t[ls].lv, t[x].rv = t[rs].rv;
t[x].inc = t[rs].inc;
if(t[rs].inc == t[rs].len && t[rs].lv > t[ls].rv) t[x].inc += t[ls].inc;
t[x].dec = t[ls].dec;
if(t[ls].dec == t[ls].len && t[ls].rv > t[rs].lv) t[x].dec += t[rs].dec;
t[x].lans = t[ls].lans;
if(t[ls].inc == t[ls].len) {
if(t[ls].rv < t[rs].lv) t[x].lans += t[rs].lans;
if(t[ls].rv > t[rs].lv) t[x].lans += t[rs].dec;
}
else if(t[ls].lans == t[ls].len && t[ls].rv > t[rs].lv) t[x].lans += t[rs].dec;
t[x].rans = t[rs].rans;
if(t[rs].dec == t[rs].len) {
if(t[rs].lv < t[ls].rv) t[x].rans += t[ls].rans;
if(t[rs].lv > t[ls].rv) t[x].rans += t[ls].inc;
}
else if(t[rs].rans == t[rs].len && t[rs].lv > t[ls].rv) t[x].rans += t[ls].inc;
t[x].ans = max(t[ls].ans, t[rs].ans);
if(t[ls].rv < t[rs].lv) t[x].ans = max(t[x].ans, t[ls].inc + t[rs].lans);
if(t[ls].rv > t[rs].lv) t[x].ans = max(t[x].ans, t[rs].dec + t[ls].rans);
}
void pushdown(int x) {
if(!t[x].tag) return;
t[ls].lv += t[x].tag, t[ls].rv += t[x].tag;
t[rs].lv += t[x].tag, t[rs].rv += t[x].tag;
t[ls].tag += t[x].tag, t[rs].tag += t[x].tag;
t[x].tag = 0;
}
void build(int x, int l, int r) {
t[x].l = l, t[x].r = r, t[x].len = r - l + 1;
t[x].lv = a[l], t[x].rv = a[r];
if(l == r) {
t[x].inc = t[x].dec = t[x].lans = t[x].rans = t[x].ans = 1;
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
pushup(x);
}
void update(int x, int l, int r, ll val) {
if(l <= t[x].l && t[x].r <= r) {
t[x].lv += val, t[x].rv += val, t[x].tag += val;
return;
}
pushdown(x);
if(l <= t[ls].r) update(ls, l, r, val);
if(t[rs].l <= r) update(rs, l, r, val);
pushup(x);
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
build(1, 1, n);
scanf("%d", &m);
for(int i = 1; i <= m; i++) {
int l, r;
ll d;
scanf("%d%d%lld", &l, &r, &d);
update(1, l, r, d);
printf("%d\n", t[1].ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话