yLOI2018 扶苏的问题

yLOI2018 扶苏的问题

线段树训练题

题意

维护序列,要求支持区间赋值,区间增量,查询区间最值。

线段树

前置知识: 线段树

区间修改,区间查询问题首先想到线段树,只是这里的线段树和普通的线段树不同,因为它有两种修改。

每个节点对区间赋值维护一个标记 \(Val\),如果它不是 \(\infty\) 那么就表示这个节点所代表的区间全部被赋成 \(Val\) 了。

每个节点对区间增量维护一个标记 \(Tag\),表示这个点的区间在上一次下传后被增加了 \(Tag\)。

两个标记优先级不同,新打的 \(Val\) 可以覆盖掉之前的 \(Tag\),但是新打的 \(Tag\) 不能覆盖之前的 \(Val\),只能叠加在 \(Val\) 上,使实际权值变成 \(Val + Tag\)。

上面两个值是通过标记下传维护的。

接下来需要有一个值 \(Mx\),表示这个节点表示区间的最大值,这个值需要通过上传维护,一个叶节点的 \(Mx\) 就是它代表单点的值,一个非叶节点的 \(Mx\) 是它两个儿子的 \(Mx\) 的最大值。

代码实现

这三种操作的递归方式和普通线段树完全相同。过程中一定要注意两个标记的优先顺序。

值得注意的是,本题的数据中,\(x\) 的绝对值是 \(10^9\) 的,操作数是 \(10^6\),也就是说单点的值可能会爆 int,所以用 long long 存储。

这种数据结构非常适合用指针写,所以这里的代码使用了指针,不要抗拒指针,学会指针对于之后学高级数据结构来说是一劳永逸的。

接下来是代码,省略了头文件和快读。

#define Wild_Donkey 0
#define INF 0x3f3f3f3f3f3f3f3f
long long Ans(0), C, a[2000005];
unsigned m, n, A, B, D;
struct Node {
  Node* LS, * RS;
  long long Mx, Val, Tag;
  inline void PsDw () {
    if(Val != INF) {
      LS->Mx = RS->Mx = LS->Val = RS->Val = Val + Tag;
      LS->Tag = RS->Tag = Tag = 0;
      Val = INF;
    } else {
      if(Tag) {
        LS->Mx += Tag, RS->Mx += Tag;
        LS->Tag += Tag, RS->Tag += Tag;
        Tag = 0;
      }
    }
  }
  inline void Chg (unsigned L, unsigned R) {
    if((A <= L) && (R <= B)) {Tag = 0, Mx = Val = C;return;}
    unsigned Mid((L + R) >> 1);
    PsDw();
    if(A <= Mid) LS->Chg(L, Mid);
    if(B > Mid) RS->Chg(Mid + 1, R);
    Mx = max(LS->Mx, RS->Mx);
  }
  inline void Inc (unsigned L, unsigned R) {
    if((A <= L) && (R <= B)) {Mx += C, Tag += C;return;}
    unsigned Mid((L + R) >> 1);
    PsDw();
    if(A <= Mid) LS->Inc(L, Mid);
    if(B > Mid) RS->Inc(Mid + 1, R);
    Mx = max(LS->Mx, RS->Mx);
  }
  inline void Qry (unsigned L, unsigned R) {
    if((A <= L) && (R <= B)) {Ans = max(Ans, Mx);return;}
    unsigned Mid((L + R) >> 1);
    PsDw();
    if(A <= Mid) LS->Qry(L, Mid);
    if(B > Mid) RS->Qry(Mid + 1, R);
  }
}N[2000005], * CntN(N);
inline void Build(Node* x, unsigned L, unsigned R) {
  x->Val = INF, x->Tag = 0;
  if(L == R) {x->Mx = x->Val = a[L]; return;}
  unsigned Mid((L + R) >> 1);
  Build(x->LS = ++CntN, L, Mid);
  Build(x->RS = ++CntN, Mid + 1, R);
  x->Mx = max(x->LS->Mx, x->RS->Mx);
}
signed main() {
  n = RD(), m = RD();
  for (unsigned i(1); i <= n; ++i) a[i] = RDsg();
  Build(N, 1, n);
  for (unsigned i(1); i <= m; ++i) {
    D = RD(), A = RD(), B = RD();
    if(D ^ 3) {
      C = RDsg();
      if(D ^ 1) N->Inc(1, n);
      else N->Chg(1, n);
    } else {
      Ans = -INF, N->Qry(1, n);
      printf("%lld\n", Ans);
    }
  }
  return Wild_Donkey;
}
上一篇:重新排列句子中的单词


下一篇:校验输入的拼音是否符合声母韵母搭配规则(不是根据汉字生成拼音)