• 线段树(三):区间修改(一个标记且有顺序)


        快速操作序列$Ⅱ$:给出一个有$n$个元素的数组$A_1,A_2,..,,A_n$,设计一个数据结构支持以下两种操作:

    • $Set(L,R,v)$:把$A_L,A_{L+1}, ..., A_R$的值全部修改成$v$($v geq 0$)
    • $Query(L,R)$:计算子序列$A_L,A_{L+1},...,A_R$的最小值

    不难想到把$set$操作进行分解,记录在结点中,但很快就会发现一个问题,add操作的时间顺序不会影响结果,但set会。比如先执行$add(1,4,1)$再执行$add(2,3,2)$等价于先执行$add(2,3,2)$再执行$add(1,4,1)$,但先执行$set(1,4,1)$再执行$set(2,3,2)$却不等价于先执行$set()2,3,2($再执行$set(1,4,1)$。怎么办呢?

        简而言之,就是维护标记,包括标记下推和标记更新。当执行$update$操作时经过带标记结点时,需要将标记传推到左右子结点,同时消除自身的标记。$set$操作的标记更新很简单,直接用新的$setv$覆盖原来的$setv$。

        值得注意的是,与之前的$add$操作相比,代码中多了两处$maintain$的调用。这是因为只要标记下传,该子树的附加信息就必须重新计算,但是我们只进入其中一个子树,该子树在递归访问结束后自然会调用$maintain$,因此还需要针对不进行递归访问的子树调用$maintain$。

     1 void pushdown(int o)
     2 {
     3     if(setv[o] >= 0)   //有标记才传递,假设set的值非负
     4     {
     5         setv[2*o] = setv[2*o+1] = setv[o];
     6         setv[o] = -1;    //清除本结点标记
     7     }
     8 }
     9 
    10 void maintain(int o, int L, int R)
    11 {
    12     if(setv[o] >= 0)  minv[o] = setv[o];
    13     else
    14     {
    15        if(L == R)  minv[o] = a[L];
    16        else  minv[o] = min(minv[2*o], minv[2*o+1]);
    17    }
    18 }
    19 
    20 int cl,cr,v;
    21 void update(int o, int L, int R)
    22 {
    23     int M=  L + (R - L) / 2;
    24     if(cl <= L && R <=cr)  setv[o] = v;
    25     else
    26     {
    27         pushdown(o);   //进入左右子树前先下推标记
    28         if(cl <= M)  update(2*o, L, M); else  maintain(2*o, L, M);
    29         if(cr > M) update(2*o+1, M + 1, R); else  maintain(2*o+1, M+1, R);
    30     }
    31   maintain(o, L, R);
    32 }

        查询操作跟之前类似,不同的是,当两个add操作存在祖先-后代关系时是进行累加,而当两个set操作存在祖先-后代关系时,以祖先结点为准即可(为什么?祖先结点的set肯定是后出现的)。

     1 int ql, qr;
     2 int query(int o, int L, int R)
     3 {
     4     int M = L + (R-L)/2;
     5     if(setv[o] >= 0)  return setv[o];
     6     if(ql <=  L && R <= qr)  return minv[o];
     7     else
     8     {
     9         int ans = INF;
    10         if(ql <= M)  ans = min(ans, query(2*o, L, M));
    11         if(qr > M)  ans = min(ans, query(2*o+1, M+1, R));
    12         return ans;
    13     }
    14 }
  • 相关阅读:
    preg_match 截取长度限制
    上传文件过大的问题
    PHP的线程安全ZTS与非线程(NTS)安全版本的区别
    开篇
    node:3416) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
    如何达到最最简单的平滑滚动
    react 复习
    包管理器
    github-443
    记录一下经常忘的数组方法,栈和队列
  • 原文地址:https://www.cnblogs.com/lfri/p/11105447.html
Copyright © 2020-2023  润新知