zoukankan      html  css  js  c++  java
  • 线段树(二):区间修改

        上一节介绍了点修改与区间查询的线段树,事实上,线段树还可以做得更多。本节讨论区间修改问题。

        给出一个$n$个元素的数组$A_1,A_2,...,A_n$,你的任务是设计一个数据结构,支持以下两种操作:

    • $Add(L,R,v)$:把$A_L,A_{L+1}, ..., A_R$的值全部增加$v$
    • $Query(L, R)$:计算子序列$A_L,A_{L+1},...,A_R$的元素和、最大值和最小值

        点修改只会影响到$logn$个结点,但区间修改在最坏情况下会影响到树中的所有结点,比如,如果对整个区间执行$add$操作,所有结点的$sum$都会发生改变。怎么办呢?

        回忆前面区间查询时的一个结论:任意区间都能分解成不超过$2h$个不相交区间。利用这个结论,我们可以“化整为零”,把一个$add$操作分解成不超过$2h$个操作,记录在线段树的结点中。

        维护的信息也需要发生改变,如果仍然用$sum[o]$表示“结点$o$对应区间中所有数的和”,则$add$操作在最坏情况下会修改所有的$sum$(不管$add$有没有分解)。解决方法是把$sum[o]$的定义改成“如果只执行结点$o$及其子孙结点的$add$操作,结点$o$对应区间中所有数之和”。这样的附加信息仍可以方便地维护,而且每个原始$add$所影响到的结点数目变成了$O(h)$。

        维护的代码如下:

     1 void maintain(int o, int L, int R)
     2 {
     3     if(L == R)  minv[o] = a[L]; //如果是叶子结点
     4     else  minv[o] = min(minv[2*o], minv[2*o+1]);    //如果是非叶子结点
     5     minv[o] += addv[o];     //考虑add操作
     6 }
     7 
     8 int cl, cr, v;  //区间修改,[cl,cr] += v;
     9 void update(int o, int L, int R)  //
    10 {
    11     int M = L + (R-L) /2;
    12     if(cl <= L && R <= cr)  addv[o] += v;
    13     else
    14     {
    15         if(cl <= M)  update(2*o, L, M);
    16         if(cr > M)  update(2*o+1, M+1, R);
    17     }
    18     maintain(o, L, R);
    19 }

        注意,此时add操作的递归边界结点的minv是对的,其子节点(即子区间)的minv是不准确的,因为没有考虑add操作的影响。

        查询操作总体上跟之前相同,但是还得考虑祖先结点对其的影响。因此,我们在递归查询中添加一个参数$add$,表示当前区间的所欲祖先结点$add$值之和(不包括本身,因为本身的add标记在maintain已考虑),方法如下:

     1 int ql, qr;     //区间查询,min{ql,qr}
     2 int query(int o, int L,int R, int add)
     3 {
     4     int M = L + (R - L) / 2;
     5     if(ql <= L && R <= qr)  return minv[o] + add;
     6     else
     7     {
     8         int ans = INF;
     9         add += addv[o];
    10         if(ql <= M)  ans = min(ans, query(2*o, L, M, add));
    11         if(qr > M)  ans = min(ans, query(2*o+1, M+1, R, add));
    12         return ans;
    13     }
    14 }

        建树的操作与点修改一模一样,不再赘述。

  • 相关阅读:
    Spark源码走读4——Scheduler
    Spark源码走读3——Job Runtime
    Spark源码走读2——Spark Submit
    Spark源码走读1——RDD
    Tachyon源码解读一:master部分
    VS2008中MFC界面编程Caption中文全是乱码的解决办法
    程序猿也爱学英语(上),有图有真相
    C++程序员必看书单
    如何将CString转换成WCHAR
    Windows 语音识别编程
  • 原文地址:https://www.cnblogs.com/lfri/p/11105222.html
Copyright © 2011-2022 走看看