题解 CF739C Alyona and towers
题意
给定一个序列,支持以下操作:
-
区间加
-
整体查询最长的单峰序列(一部分严格单调递增,另一部分严格单调递减)。
思路
线段树的简单题,做法和思路基本和 P4513 差不多,难点在于怎么上传整合各子区间的信息。
为方便处理左右两个子区间的边界问题,维护区间内以下 9 个量:
-
左端点权值 $\text{lv}$。
-
右端点权值 $\text{rv}$。
-
从左端点开始的最长下降序列 $\text{dec}$。
-
从右端点结束的最长上升序列 $\text{inc}$。
-
从左端点开始的最长的单峰序列 $\text{lans}$。
-
从右端点结束的最长的单峰序列 $\text{rans}$。
-
区间内最长的单峰序列 $\text{ans}$。
-
区间加的懒标记 $\text{tag}$。
-
区间长度 $\text{len}$。
懒标记的下传很常规,修改 $\text{lv}$ 和 $\text{rv}$ 即可。
重点是上传的部分。
定义 $ls$ 为左区间,$rs$ 为右区间。
分类讨论:
-
对于 $\text{lv}$ 和 $\text{rv}$ 直接继承左右区间即可。
-
对于 $\text{dec}$,分两类讨论:
- 若 $\text{dec}_{ls} = \text{len}_{ls}$ 且 $\text{lv}_{rs}>\text{rv}_{ls}$,说明下降序列一直延续到了右区间,$\text{dec}\gets \text{dec}_{ls}+\text{dec}_{rs}$。
- 若 $\text{dec}_{ls} \not= \text{len}_{ls}$ 或 $\text{lv}_{rs}\leq\text{rv}_{ls}$,$\text{dec}\gets \text{dec}_{ls}$。
-
对于 $\text{inc}$,分两类讨论:
- 若 $\text{inc}_{rs} = \text{len}_{rs}$ 且 $\text{rv}_{ls}<\text{lv}_{rs}$,说明上升序列一直延续到了左区间,$\text{inc}\gets \text{inc}_{rs}+\text{inc}_{ls}$。
- 若 $\text{inc}_{rs} \not= \text{len}_{rs}$ 或 $\text{rv}_{ls}\ge\text{lv}_{rs}$,$\text{inc}\gets \text{inc}_{rs}$。
-
对于 $\text{lans}$,若 $\text{inc}_{ls}=\text{len}_{ls}$,说明左区间是一个上升序列,否则直接继承左子树即可,分五类讨论:
- 若 $\text{rv}_{ls}=\text{lv}_{rs}$,此时答案延续不到右区间,$\text{lans}\gets\text{lans}_{ls}$。
- 若 $\text{inc}_{ls}=\text{len}_{ls}$ 且 $\text{rv}_{ls}<\text{lv}_{rs}$,说明左区间的上升序列在右区间还可延续,$\text{lans}\gets\text{lans}_{ls}+\text{lans}_{rs}$。
- 若 $\text{inc}_{ls}=\text{len}_{ls}$ 且 $\text{rv}_{ls}>\text{lv}_{rs}$,说明左区间的上升序列在右区间不可延续,右区间只可下降,$\text{lans}\gets\text{lans}_{ls}+\text{dec}_{rs}$。
- 若 $\text{lans}_{ls}=\text{len}_{ls}$ 且 $\text{rv}_{ls}>\text{lv}_{rs}$,说明左区间可能可以令右区间下降增加长度,$\text{lans}=\text{lans}_{ls}+\text{dec}_{rs}$。
- 其余情况说明左区间无法连接到右区间,$\text{lans}\gets\text{lans}_{ls}$。
-
$\text{rans}$ 也类似的处理。
-
对于 $\text{ans}$,有三种继承的情况:
- 从 $\text{ans}_{ls}$ 和 $\text{ans}_{rs}$ 继承来,$\text{ans}\gets\max(\text{ans}_{ls},\text{ans}_{rs})$。
- 若 $\text{rv}_{ls}>\text{lv}_{rs}$,此时最大值在左区间,$\text{ans}\gets\text{rans}_{ls}+\text{dec}_{rs}$。
- 若 $\text{rv}_{ls}<\text{lv}_{rs}$,此时最大值在右区间,$\text{ans}\gets\text{lans}_{rs}+\text{inc}_{ls}$。
其他操作就和普通线段树一样了。
本题还有一个稍微简单些的做法,维护一个差分数组,每次整体查询由相邻的连续正数段和连续负数段构成的最长子段,这样每次修改就变成单点加,不需要区间加的懒标记了,维护这个问题的操作和朴素的线段树差不多。
时间复杂度为线段树的 $n\log n$。
这个数据范围要开 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;
}

浙公网安备 33010602011771号