• 【暑假】[实用数据结构]动态范围查询问题


    动态范围查询问题:

     一、线段树+点修改

      支持操作:

    1. Update(x,v): 将Ax修改为v
    2. Query(L,R) : 计算[L,R]内的最小值

     

     1 int minv[maxn];
     2 int ql,qr;
     3 int Query(int u,int L,int R){
     4     int M=L + (R-L)/2 , ans=INF;
     5     if(ql<=L && R<=qr) return minv[u];
     6     if(ql <= M) ans=min(ans,Query(2*u,L,M));  
     7     if(M < qr) ans=min(ans,Query(2*u+1,M+1,R));
     8     return ans;
     9 } 
    10 
    11 int p,v; //A[p]=v
    12 void Update(int u,int L,int R){
    13     if(L==R) {minv[u]=v; return; } //叶节点则修改 
    14     int M=L+(R-L)+1;
    15     if(p<=M) Update(2*u,L,M); else Update(2*u+1,M+1,R); //递归p所在子树 
    16     minv[u]=min(minv[2*u],minv[2*u+1]); //更新当前minv 
    17 }

    联系题目:LA3938

    链接:

        

     二、线段树+区间修改

       快速序列操作1:

         支持操作:

    1.   Add(L,R,v):
    2.   Query(L,R):计算[L,R]内的最小值、最大值、区间和。

         关于算法:在的基础上增加了addv,这种为了避免复杂操作而打标记的方法与链表题UVa12657相通,新增maintain维护结点信息。需要注意的是Query的新参数add是记录根到“叶子”路径上祖先的add之和。

     1 int minv[maxn],addv[maxn],sumv[maxn],maxv[maxn];
     2 //maintain维护u的结点信息 
     3 void maintain(int u,int L,int R){
     4     int lc=2*u,rc=2*u+1;
     5     minv[u]=maxv[u]=sumv[u]=0;  //叶子结点的设置 
     6     if(L<R){ //有子树 
     7         minv[u]=min(minv[lc],minv[rc]);
     8         maxv[u]=max(maxv[lc],maxv[rc]);
     9         sumv[u]=sumv[lc]+sumv[rc];
    10     }
    11     minv[u] += addv[u]; maxv[u] += addv[u]; sumv[u] += addv[u]*(R-L+1); 
    12     //亦 叶子结点 
    13     //考虑到add 
    14 }
    15 
    16 
    17 int y1,y2,v; //A[y1~y2] += v
    18 void Add(int u,int L,int R){
    19     if(y1<=L && R<=y2)  addv[u] +=v;
    20     else{
    21       int lc=2*u,rc=2*u+1;    
    22       int M=L+(R-L)+1;
    23       if(y1 <= M) Add(lc,L,M); 
    24       if(M < y2) Add(rc,M+1,R);
    25     }
    26     maintain(u,L,R); //递归结束后维护u结点信息 
    27 }
    28 
    29 int ql,qr;
    30 int _min=INF,_max=-INF,_sum=0; 
    31 int Query(int u,int L,int R,int add){ //要考虑到祖先的add 
    32    if(ql<=L && R<=qr) {
    33        _min=min(_min,minv[u]+add);
    34        _max=max(_max,maxv[u]+add);
    35        _sum += sumv[u]+ add*(R-L+1);
    36    }
    37   else{
    38       int M=L+(R-L)/2;
    39       if(ql <= M) Query(u*2,L,M,add+addv[u]); //add+addv[u]保证是根->叶该路径上的祖先add之和 
    40       if(M < qr) Query(u*2+1,M+1,R,add+addv[u]);
    41   } 
    42 } 

      快速序列操作2:

          支持操作:

    1. Set(L,R,v) : A[L~R]=v
    2. Query(L,R): 计算[L,R]内的最小值、最大值、区间和

         关于算法:注意到 1 类题目的Add操作对于顺序是没有要求的,而 2 类题目中的Set则是有顺序要求,如果顺序改变结果亦会改变。所以泛泛地说:任意两个Set操作不能出现祖先后代的关系。所以必要时需要把Set下传给子结点。 但也可以找到set是祖先后辈关系的反例,这种情况是两操作并未“相遇”的结果,因为只有“相遇”才会pushdown。要知道处于上方的操作是下方操作之后标记的,于是加入递归边界1:有无set标记的判断。

    int minv[maxn],setv[maxn],sumv[maxn],maxv[maxn];
    //maintain维护u的结点信息 
    void maintain(int u,int L,int R){
        int lc=2*u,rc=2*u+1;
        minv[u]=maxv[u]=sumv[u]=0;  //叶子结点的设置 
        if(L<R){ //有子树 
            minv[u]=min(minv[lc],minv[rc]);
            maxv[u]=max(maxv[lc],maxv[rc]);
            sumv[u]=sumv[lc]+sumv[rc];
        }
        if(setv[u] >= 0) { minv[u]=maxv[u]=setv[u];sumv[u]=setv[u]*(R-L+1);} 
    }
    
    
    void pushdown(int u){
        int lc=u*2,rc=u*2+1;
        if(setv[u]>=0){
            setv[lc]=setv[rc]=setv[u]; //下传 -1表示无set 
            setv[u]=-1;
        }
    }
    int y1,y2,v; //A[y1~y2] += v
    void Set(int u,int L,int R){
        if(y1<=L && R<=y2)  setv[u]=v;
        else{
          pushdown(u);  //下传set 
          int lc=2*u,rc=2*u+1;    
          int M=L+(R-L)+1;
          if(y1 <= M) Set(lc,L,M); else maintain(lc,L,M);
          if(M < y2) Set(rc,M+1,R); else maintain(rc,M+1,R);
          //针对不能递归的子树的两次maintain 维护信息 
        }
        maintain(u,L,R); //递归结束后维护u结点信息 
    }
    
    int ql,qr;
    int _min=INF,_max=-INF,_sum=0; 
    int Query(int u,int L,int R){ 
        if(setv[u]>=0){            //递归边界1 有set标记  //注意sum范围 
            _sum += setv[u] * (min(R,qr)-max(L,ql)+1); 
            _min=min(_min,minv[u]);
            _max=max(_max,maxv[u]);
        }
        else if(ql<=L && R<=qr){ //递归边界2 边界区间 且 此区间没有受到set的影响 
            _sum +=  sumv[u];
            _min=min(_min,minv[u]);
            _max=max(_max,maxv[u]);
        } else{
            int M=L+(R-L)/2;
            if(ql<=M) Query(u*2,L,M);
            if(M < qr) Query(u*2+1,M+1,R);
        }
    } 

    作者所给同时支持Add与Set操作的模板

    struct IntervalTree {
      int sumv[maxnode], minv[maxnode], maxv[maxnode], setv[maxnode], addv[maxnode];
    
      // 维护信息
      void maintain(int o, int L, int R) {
        int lc = o*2, rc = o*2+1;
        if(R > L) {
          sumv[o] = sumv[lc] + sumv[rc];
          minv[o] = min(minv[lc], minv[rc]);
          maxv[o] = max(maxv[lc], maxv[rc]);
        }
        if(setv[o] >= 0) { minv[o] = maxv[o] = setv[o]; sumv[o] = setv[o] * (R-L+1); }
        if(addv[o]) { minv[o] += addv[o]; maxv[o] += addv[o]; sumv[o] += addv[o] * (R-L+1); }
      }
    
      // 标记传递
      void pushdown(int o) {
        int lc = o*2, rc = o*2+1;
        if(setv[o] >= 0) {
          setv[lc] = setv[rc] = setv[o];
          addv[lc] = addv[rc] = 0;
          setv[o] = -1; // 清除本结点标记
        }
        if(addv[o]) {
          addv[lc] += addv[o];
          addv[rc] += addv[o];
          addv[o] = 0; // 清除本结点标记
        }
      }
    
      void update(int o, int L, int R) {
        int lc = o*2, rc = o*2+1;
        if(y1 <= L && y2 >= R) { // 标记修改      
          if(op == 1) addv[o] += v;
          else { setv[o] = v; addv[o] = 0; }
        } else {
          pushdown(o);
          int M = L + (R-L)/2;
          if(y1 <= M) update(lc, L, M); else maintain(lc, L, M);
          if(y2 > M) update(rc, M+1, R); else maintain(rc, M+1, R);
        }
        maintain(o, L, R);
      }
    
      void query(int o, int L, int R, int add) {
        if(setv[o] >= 0) {
          int v = setv[o] + add + addv[o];
          _sum += v * (min(R,y2)-max(L,y1)+1);
          _min = min(_min, v);
          _max = max(_max, v);
        } else if(y1 <= L && y2 >= R) {
          _sum += sumv[o] + add * (R-L+1);
          _min = min(_min, minv[o] + add);
          _max = max(_max, maxv[o] + add);
        } else {
          int M = L + (R-L)/2;
          if(y1 <= M) query(o*2, L, M, add + addv[o]);
          if(y2 > M) query(o*2+1, M+1, R, add + addv[o]);
        }
      }
    };

    联系题目:UVa11992

    链接:

    如果代码中包含中文字符会乱码 ┑( ̄Д  ̄)┍

  • 相关阅读:
    java cocurrent并发包
    阻塞队列只有一个线程在同一时刻对其进行或者读或者写
    在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
    深入理解生产者消费者
    java并发编程阻塞队列
    高并发
    ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗
    JAVA设计模式之工厂模式(简单工厂模式+工厂方法模式)
    Java并发编程-Executor框架(转)
    Java主线程等待所有子线程执行完毕再执行解决办法(转)
  • 原文地址:https://www.cnblogs.com/lidaxin/p/4708860.html
Copyright © 2020-2023  润新知