区间历史最值线段树记录

区间+/chkmin/求最值/求历史最值

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

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;
}

区间+/赋值/求最值/求历史最值

Description

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

Solution

如果没有区间覆盖就像刚才一样维护两个 tag。但是如果加上区间覆盖后由于 tag 维护的是初始到终止的整体位移量,而只要一个节点被区间赋值后所有数就变得相等,那么这个位移量就没用了。

所以考虑对于每个节点,维护做区间赋值之前的两个 tag,然后维护做区间赋值后的当前赋的值和赋的值的最大值。

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

Code

Code
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 1e5 + 5;

int n, m;
int a[kMaxN];

struct SGT {
  int maxa[kMaxN * 4], maxb[kMaxN * 4];
  int tag1[kMaxN * 4], tag2[kMaxN * 4], tagcov1[kMaxN * 4], tagcov2[kMaxN * 4];

  void pushup(int x) {
    maxa[x] = std::max(maxa[x << 1], maxa[x << 1 | 1]);
    maxb[x] = std::max(maxb[x << 1], maxb[x << 1 | 1]);
  }

  void addtag1(int x, int v1, int v2) {
    maxb[x] = std::max(maxb[x], maxa[x] + v2);
    maxa[x] += v1;
    if (tagcov1[x] == -1e18) {
      tag2[x] = std::max(tag2[x], tag1[x] + v2), tag1[x] += v1;
    } else {
      tagcov2[x] = std::max(tagcov2[x], tagcov1[x] + v2), tagcov1[x] += v1;
    }
  }

  void addtag2(int x, int v1, int v2) {
    maxb[x] = std::max(maxb[x], v2);
    maxa[x] = v1;
    tagcov2[x] = std::max(tagcov2[x], v2);
    tagcov1[x] = v1;
  }

  void pushdown(int x) {
    if (tag1[x] || tag2[x]) {
      addtag1(x << 1, tag1[x], tag2[x]);
      addtag1(x << 1 | 1, tag1[x], tag2[x]);
      tag1[x] = tag2[x] = 0;
    }
    if (tagcov1[x] != -1e18 || tagcov2[x] != -1e18) {
      addtag2(x << 1, tagcov1[x], tagcov2[x]);
      addtag2(x << 1 | 1, tagcov1[x], tagcov2[x]);
      tagcov1[x] = tagcov2[x] = -1e18;
    }
  }

  void build(int x, int l, int r) {
    tagcov1[x] = tagcov2[x] = -1e18;
    if (l == r) {
      maxa[x] = maxb[x] = a[l];
      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 addtag1(x, v, std::max<int>(0, v));
    pushdown(x);
    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) {
    if (l > qr || r < ql) return;
    else if (l >= ql && r <= qr) return addtag2(x, v, v);
    pushdown(x);
    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);
  }

  int querymaxa(int x, int l, int r, int ql, int qr) {
    if (l > qr || r < ql) return -1e18;
    else if (l >= ql && r <= qr) return maxa[x];
    pushdown(x);
    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 -1e18;
    else if (l >= ql && r <= qr) return maxb[x];
    pushdown(x);
    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;
  for (int i = 1; i <= n; ++i) std::cin >> a[i];
  sgt.build(1, 1, n);
  std::cin >> m;
  for (int i = 1; i <= m; ++i) {
    std::string op;
    int l, r, v;
    std::cin >> op >> l >> r;
    if (op[0] == 'Q') {
      std::cout << sgt.querymaxa(1, 1, n, l, r) << '\n';
    } else if (op[0] == 'A') {
      std::cout << sgt.querymaxb(1, 1, n, l, r) << '\n';
    } else if (op[0] == 'P') {
      std::cin >> v;
      sgt.update1(1, 1, n, l, r, v);
    } else {
      std::cin >> v;
      sgt.update2(1, 1, n, l, r, v);
    }
  }
}

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;
}
posted @ 2024-08-12 20:33  下蛋爷  阅读(4)  评论(0编辑  收藏  举报