专栏文章

题解:P13825 线段树 1.5

P13825题解参与者 17已保存评论 21

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
21 条
当前快照
1 份
快照标识符
@mio6226p
此快照首次捕获于
2025/12/02 13:57
3 个月前
此快照最后确认于
2025/12/02 13:57
3 个月前
查看原文

Analysis

首先问题可以变成一个初始全 00 的序列按要求做操作,把每次查询的答案加上 [l,r][l,r] 的原区间和,即 (rl+1)(l+r)2\frac{(r-l+1)(l+r)}{2}
然后考虑这个对初始全 00 序列做区间加和区间查询的操作,如果 nn 很小,那就可以直接用普通的线段树(P3372)来解决。
假设我们能把这个 nn 很大的线段树建出来,注意到初始时所有节点的值(包括区间和以及 lazy tag)都是 00。一次修改和查询操作只会访问到树上 O(logn)O(\log n) 个节点。显然只有被访问过的节点才有可能被修改值。所以 mm 次操作结束后,这棵树最多只有 O(mlogn)O(m \log n) 个节点的值是非 00 的。
我们只维护非 00 的节点。对于一个节点,如果它的某个孩子指针为空,则表示该孩子和子树的值均为 00。在最开始,只创建根节点。注意到每次线段树向下递归时总会先进行一次 pushdown,所以只需要在 pushdown 时提前把空的左右孩子创建出来,就可以保证访问到的节点总是非空的。并且因为一共只进行了 O(mlogn)O(m \log n) 次 pushdown,所以创建的总节点数也是 O(mlogn)O(m \log n)
注意到这样的写法仍然创建了一些仅用于查询的(本身没被修改过,即值为 00)的节点。更精细的写法可以只创建真正被修改过的节点,在查询时手动跳过值为 00 的递归。但是在大部分情况下(查询数和修改数同阶),二者创建的节点数差别并不大,本文的写法已经足够了。
注意要开 unsiged long long。

Code

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

using ll = unsigned long long;

struct Node {
  Node *ls, *rs;
  int l, r;
  ll val, tag;

  Node(int L, int R) : l(L), r(R), val(0), tag(0) {
    ls = rs = nullptr;
  }

  void pushup() { val = ls->val + rs->val; }
  bool inRange(int L, int R) { return L <= l && r <= R; }
  bool outRange(int L, int R) { return r < L || R < l; }
  inline void makeTag(ll x) {
    val += (r - l + 1) * x;
    tag += x;
  }
  inline void pushdown() {
    if (!ls) {
      int mid = (l + r) >> 1;
      ls = new Node(l, mid);
      rs = new Node(mid + 1, r);
    }
    if (tag) {
      ls->makeTag(tag);
      rs->makeTag(tag);
      tag = 0;
    }
  }

  void upd(int L, int R, ll k) {
    if (inRange(L, R)) {
      makeTag(k);
    } else if (!outRange(L, R)) {
      pushdown();
      ls->upd(L, R, k);
      rs->upd(L, R, k);
      pushup();
    }
  }

  ll qry(int L, int R) {
    if (inRange(L, R)) {
      return val;
    } else if (outRange(L, R)) {
      return 0;
    } else {
      pushdown();
      return ls->qry(L, R) + rs->qry(L, R);
    }
  }
};

int main() {
  int n, q;
  std::cin >> n >> q;
  auto rot = new Node(1, n);
  for (int op, l, r; q; --q) {
    std::cin >> op >> l >> r;
    if (op == 1) {
      ll k;
      std::cin >> k;
      rot->upd(l, r, k);
    } else {
      std::println("{}", rot->qry(l, r) + [](ll L, ll R) -> ll {
        return (R - L + 1) * (R + L) / 2;
      }(l, r));
    }
  }
}

评论

21 条评论,欢迎与作者交流。

正在加载评论...