题解 CF739C Alyona and towers

$\texttt{Link}$

题意

给定一个序列,支持以下操作:

  • 区间加

  • 整体查询最长的单峰序列(一部分严格单调递增,另一部分严格单调递减)。

思路

线段树的简单题,做法和思路基本和 P4513 差不多,难点在于怎么上传整合各子区间的信息。

为方便处理左右两个子区间的边界问题,维护区间内以下 9 个量:

  1. 左端点权值 $\text{lv}$。

  2. 右端点权值 $\text{rv}$。

  3. 从左端点开始的最长下降序列 $\text{dec}$。

  4. 从右端点结束的最长上升序列 $\text{inc}$。

  5. 从左端点开始的最长的单峰序列 $\text{lans}$。

  6. 从右端点结束的最长的单峰序列 $\text{rans}$。

  7. 区间内最长的单峰序列 $\text{ans}$。

  8. 区间加的懒标记 $\text{tag}$。

  9. 区间长度 $\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;
}
posted @ 2021-05-05 17:03  Terac  阅读(21)  评论(0)    收藏  举报  来源