题解 CF739C Alyona and towers

Link

题意

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

  • 区间加

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

思路

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

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

  1. 左端点权值 lv

  2. 右端点权值 rv

  3. 从左端点开始的最长下降序列 dec

  4. 从右端点结束的最长上升序列 inc

  5. 从左端点开始的最长的单峰序列 lans

  6. 从右端点结束的最长的单峰序列 rans

  7. 区间内最长的单峰序列 ans

  8. 区间加的懒标记 tag

  9. 区间长度 len

懒标记的下传很常规,修改 lvrv 即可。

重点是上传的部分。

定义 ls 为左区间,rs 为右区间。

分类讨论:

  • 对于 lvrv 直接继承左右区间即可。

  • 对于 dec,分两类讨论:

    • decls=lenlslvrs>rvls,说明下降序列一直延续到了右区间,decdecls+decrs
    • declslenlslvrsrvlsdecdecls
  • 对于 inc,分两类讨论:

    • incrs=lenrsrvls<lvrs,说明上升序列一直延续到了左区间,incincrs+incls
    • incrslenrsrvlslvrsincincrs
  • 对于 lans,若 incls=lenls,说明左区间是一个上升序列,否则直接继承左子树即可,分五类讨论:

    • rvls=lvrs,此时答案延续不到右区间,lanslansls
    • incls=lenlsrvls<lvrs,说明左区间的上升序列在右区间还可延续,lanslansls+lansrs
    • incls=lenlsrvls>lvrs,说明左区间的上升序列在右区间不可延续,右区间只可下降,lanslansls+decrs
    • lansls=lenlsrvls>lvrs,说明左区间可能可以令右区间下降增加长度,lans=lansls+decrs
    • 其余情况说明左区间无法连接到右区间,lanslansls
  • rans 也类似的处理。

  • 对于 ans,有三种继承的情况:

    • anslsansrs 继承来,ansmax(ansls,ansrs)
    • rvls>lvrs,此时最大值在左区间,ansransls+decrs
    • rvls<lvrs,此时最大值在右区间,anslansrs+incls

其他操作就和普通线段树一样了。

本题还有一个稍微简单些的做法,维护一个差分数组,每次整体查询由相邻的连续正数段和连续负数段构成的最长子段,这样每次修改就变成单点加,不需要区间加的懒标记了,维护这个问题的操作和朴素的线段树差不多。

时间复杂度为线段树的 nlogn

这个数据范围要开 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 @   Terac  阅读(12)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示