首页 > 其他分享 >区间历史最值线段树记录

区间历史最值线段树记录

时间:2024-08-12 20:40:32浏览次数:8  
标签:std return int 线段 kMaxN 区间 ql 最值

Description

维护一个线段树,使得可以实现区间加、区间 chkmin、求区间最值、区间历史最值、区间最大值。

Solution

先不考虑区间 chkmin 和历史最值,可以直接对于每个线段树节点维护一个 tag,每次 addtag 更新。

加上区间历史最值后,先考虑对于单个线段树节点怎么更新。

容易发现对于一个节点,在它下传标记之前一定就是很多次整体加某个数,并且儿子的 \(sum\) 和 \(max\) 数组是不会变的。

考虑维护 \(tag2[x]\) 表示 \(x\) 这个节点上次下传标记到当前时刻的最大前缀和,那么每次下传标记时先让 \(maxb[son]\leftarrow maxa[son]+tag2[x]\) 再更新 \(maxa[son]\) 就可以维护区间历史最值了。

加上区间 chkmin 就用吉司机线段树的技巧维护区间最大值、次大值,并且由于这里最大值和非最大值有些操作是分开的,所以需要对于最大、非最大值分别维护 tag。

时间复杂度:\(O(n\log^2 n)\)。

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 5e5 + 5;

int n, m;
int a[kMaxN];

struct SGT {
  int maxa[kMaxN * 4], maxb[kMaxN * 4];
  int cnt[kMaxN * 4], se[kMaxN * 4];
  int tag1[kMaxN * 4], tag2[kMaxN * 4], tag3[kMaxN * 4], tag4[kMaxN * 4];
  int64_t sum[kMaxN * 4];

  void pushup(int x) {
    sum[x] = sum[x << 1] + sum[x << 1 | 1];
    maxa[x] = std::max(maxa[x << 1], maxa[x << 1 | 1]);
    maxb[x] = std::max(maxb[x << 1], maxb[x << 1 | 1]);
  
    if (maxa[x << 1] == maxa[x << 1 | 1]) {
      se[x] = std::max(se[x << 1], se[x << 1 | 1]);
      cnt[x] = cnt[x << 1] + cnt[x << 1 | 1];
    } else if (maxa[x << 1] > maxa[x << 1 | 1]) {
      se[x] = std::max(se[x << 1], maxa[x << 1 | 1]);
      cnt[x] = cnt[x << 1];
    } else {
      se[x] = std::max(maxa[x << 1], se[x << 1 | 1]);
      cnt[x] = cnt[x << 1 | 1];
    }
  }

  void addtag(int x, int l, int r, int v1, int v2, int v3, int v4) {
    maxb[x] = std::max(maxb[x], maxa[x] + v2);
    tag2[x] = std::max(tag2[x], tag1[x] + v2);
    tag4[x] = std::max(tag4[x], tag3[x] + v4);
    maxa[x] += v1, tag1[x] += v1, tag3[x] += v3;
    sum[x] += 1ll * cnt[x] * v1 + 1ll * (r - l + 1 - cnt[x]) * v3;
    if (se[x] != -1e18) se[x] += v3;
  }

  void pushdown(int x, int l, int r) {
    if (tag1[x] || tag2[x] || tag3[x] || tag4[x]) {
      int mx = std::max(maxa[x << 1], maxa[x << 1 | 1]);
      int mid = (l + r) >> 1;
      if (maxa[x << 1] == mx) {
        addtag(x << 1, l, mid, tag1[x], tag2[x], tag3[x], tag4[x]);
      } else {
        addtag(x << 1, l, mid, tag3[x], tag4[x], tag3[x], tag4[x]);
      }
      if (maxa[x << 1 | 1] == mx) {
        addtag(x << 1 | 1, mid + 1, r, tag1[x], tag2[x], tag3[x], tag4[x]);
      } else {
        addtag(x << 1 | 1, mid + 1, r, tag3[x], tag4[x], tag3[x], tag4[x]);
      }
      tag1[x] = tag2[x] = tag3[x] = tag4[x] = 0;
    }
  }

  void build(int x, int l, int r) {
    se[x] = -2e9;
    if (l == r) {
      sum[x] = maxa[x] = maxb[x] = a[l], cnt[x] = 1;
      return;
    }
    int mid = (l + r) >> 1;
    build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
    pushup(x);
  }

  void update1(int x, int l, int r, int ql, int qr, int v) { // 区间 +
    if (l > qr || r < ql) return;
    else if (l >= ql && r <= qr) return addtag(x, l, r, v, std::max<int>(v, 0), v, std::max<int>(v, 0));
    pushdown(x, l, r);
    int mid = (l + r) >> 1;
    update1(x << 1, l, mid, ql, qr, v), update1(x << 1 | 1, mid + 1, r, ql, qr, v);
    pushup(x);
  }

  void update2(int x, int l, int r, int ql, int qr, int v) { // 区间 chkmin
    if (l > qr || r < ql || maxa[x] <= v) return;
    else if (l >= ql && r <= qr && se[x] < v)
      return addtag(x, l, r, v - maxa[x], 0, 0, 0);
    pushdown(x, l, r);
    int mid = (l + r) >> 1;
    update2(x << 1, l, mid, ql, qr, v), update2(x << 1 | 1, mid + 1, r, ql, qr, v);
    pushup(x);
  }

  int64_t querysum(int x, int l, int r, int ql, int qr) {
    if (l > qr || r < ql) return 0;
    else if (l >= ql && r <= qr) return sum[x];
    pushdown(x, l, r);
    int mid = (l + r) >> 1;
    return querysum(x << 1, l, mid, ql, qr) + querysum(x << 1 | 1, mid + 1, r, ql, qr);
  }

  int querymaxa(int x, int l, int r, int ql, int qr) {
    if (l > qr || r < ql) return -2e9;
    else if (l >= ql && r <= qr) return maxa[x];
    pushdown(x, l, r);
    int mid = (l + r) >> 1;
    return std::max(querymaxa(x << 1, l, mid, ql, qr), querymaxa(x << 1 | 1, mid + 1, r, ql, qr));
  }

  int querymaxb(int x, int l, int r, int ql, int qr) {
    if (l > qr || r < ql) return -2e9;
    else if (l >= ql && r <= qr) return maxb[x];
    pushdown(x, l, r);
    int mid = (l + r) >> 1;
    return std::max(querymaxb(x << 1, l, mid, ql, qr), querymaxb(x << 1 | 1, mid + 1, r, ql, qr));
  }
} sgt;

void dickdreamer() {
  std::cin >> n >> m;
  for (int i = 1; i <= n; ++i) std::cin >> a[i];
  sgt.build(1, 1, n);
  for (int i = 1; i <= m; ++i) {
    int op, l, r, v;
    std::cin >> op >> l >> r;
    if (op == 1) {
      std::cin >> v;
      sgt.update1(1, 1, n, l, r, v);
    } else if (op == 2) {
      std::cin >> v;
      sgt.update2(1, 1, n, l, r, v);
    } else if (op == 3) {
      std::cout << sgt.querysum(1, 1, n, l, r) << '\n';
    } else if (op == 4) {
      std::cout << sgt.querymaxa(1, 1, n, l, r) << '\n';
    } else {
      std::cout << sgt.querymaxb(1, 1, n, l, r) << '\n';
    }
  }
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:std,return,int,线段,kMaxN,区间,ql,最值
From: https://www.cnblogs.com/Scarab/p/18355696

相关文章

  • 线段树水题集合
    用的都是《算法竞赛》的码风哦洛谷P3870极简,开和关用10表示,然后区间修改,区间求和,裸题洛谷P2846这两道题一模一样#include<bits/stdc++.h>usingnamespacestd;constintN=100005;#definelllonglongllls(llp){returnp<<1;}llrs(llp){returnp<<1|1;}lln......
  • 洛谷 P4556 雨天的尾巴之线段树合并模板
    洛谷P4556题解传送锚点摸鱼环节[Vani有约会]雨天的尾巴/【模板】线段树合并题目背景深绘里一直很讨厌雨天。灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以......
  • zkw线段树
    介绍非递归线段树实现方法,码量较短。zkw线段树的构造原理:普通线段树采用堆存储,zkw线段树本质上是满二叉树(若没有该区间则为空点)但根据实际情况,原区间不一定构成满二叉树,据查询方式限制,空间开到最接近的\(2^n\)(据性质树值域=底层节点数),即不存在的点有虚点填充。既然不......
  • 力扣第五十七题——插入区间
    内容介绍给你一个 无重叠的 ,按照区间起始端点排序的区间列表 intervals,其中 intervals[i]=[starti,endi] 表示第 i 个区间的开始和结束,并且 intervals 按照 starti 升序排列。同样给定一个区间 newInterval=[start,end] 表示另一个区间的开始和结束。在......
  • 关于区间加区间查线段树的标记永久化
    单点加区间查的线段树,每个线段树区间的值代表所维护序列在这个区间的总和;区间加单点查的线段树,每个线段树区间的值代表对这个区间总体加了多少。区间加区间查的线段树可以通过综合两种思想实现标记的永久化。线段树将每一个修改或查询区间拆分为\(O(\logw)\)个线段树区间,只要......
  • 李超线段树
    P4097【模板】李超线段树/[HEOI2013]Segment-洛谷|计算机科学教育新生态(luogu.com.cn)要求在平面直角坐标系下维护两个操作:在平面上加入一条线段。记第\(i\)条被插入的线段的标号为\(i\)。给定一个数\(k\),询问与直线\(x=k\)相交的线段中,交点纵坐标最大的线......
  • 线段树:线段树的定义和应用
    引言线段树是一种高级数据结构,用于解决区间查询和更新问题。它在许多应用中都有广泛的使用,例如数组区间的求和、最小值/最大值查询、区间的最小公倍数/最大公约数查询等。本文将详细介绍线段树的定义、构建、应用以及代码实现。目录线段树的定义线段树的应用线段树的构建......
  • P3834 【模板】可持久化线段树 2
    原题链接题解总体上来讲,就是二分\(x\)查询插入\(l-1\)时有多少数小于等于\(x\),查询插入\(r\)时有多少数小于等于\(x\)然后减一减,看看是不是小于等于\(k-1\)我认为目前没有比ai讲的更清楚的了,请点击这里code#include<bits/stdc++.h>usingnamespacestd;/*#def......
  • 线段树优化建图
    今天写了道线段树优化建图的板子题,感觉学算法的还是要记录下来,将来方便复习也算是对竞赛生涯的一种回忆http://codeforces.com/problemset/problem/786/B,洛谷评级还是省选,如果是比赛现场想出来确实厉害,但是现在嘛,已经是时代眼泪了归纳下特点:和区间有关的图论问题直接上代码讲解......
  • 刍议线段树 2 (区间修改,区间查询)
    线段树\(2\)接上一讲https://www.cnblogs.com/yingxilin/p/18350988(没看的同学们可以先看这篇)上一讲里我们已经介绍了单点修改,区间查询的线段树了。在这一讲里,我们开始学习支持区间修改,区间查询的线段树。考虑之前的做法,之前的查询区间会被分为\(O(logn)\),从而求解,但因为......