• 线段树模板(含区间最大(小)值)


    概述

    线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)O(logn)。

    (0) 定义

    1 const int INF = 0x3f3f3f3f;
    2 const int MAXN = 50010;
    3 int a[MAXN],ans[MAXN<<2],lazy[MAXN<<2];
    4 // a[]为原序列信息,ans[]模拟线段树维护区间和,lazy[]为懒惰标记
    5 int mx[MAXN<<2],mn[MAXN<<2];
    6 // mx[]区间最大值 mn[]区间最小值

    (1) 更新结点信息

    1 void PushUp(int rt)
    2 {
    3     ans[rt] = ans[rt<<1] + ans[rt<<1|1];   // 区间和
    4     mx[rt] = max(mx[rt<<1],mx[rt<<1|1]);   // 区间最大值
    5     mn[rt] = min(mn[rt<<1],mn[rt<<1|1]);   // 区间最小值
    6 }

    (2) 建树

     1 void Build(int l,int r,int rt)
     2 {
     3     if(l == r)
     4     {
     5         mx[rt] = mn[rt] = ans[rt] = a[l];
     6         return ;
     7     }
     8     int mid = (l+r)>>1;
     9     Build(l,mid,rt<<1);
    10     Build(mid+1,r,rt<<1|1);
    11     PushUp(rt);
    12 }

    (3) 下推懒惰标记

     1 void PushDown(int rt,int ln,int rn)   
     2 // ln表示左子树元素结点个数,rn表示右子树结点个数
     3 {
     4     if(lazy[rt])
     5     {
     6         lazy[rt<<1] += lazy[rt];
     7         lazy[rt<<1|1] += lazy[rt];
     8         ans[rt<<1] += lazy[rt]*ln;
     9         ans[rt<<1|1] += lazy[rt]*rn;
    10         mx[rt<<1] += lazy[rt];
    11         mx[rt<<1|1] += lazy[rt];
    12         mn[rt<<1] += lazy[rt];
    13         mn[rt<<1|1] += lazy[rt];
    14         lazy[rt] = 0;
    15     }
    16 }

    (4) 点更新

     1 void Add(int L,int C,int l,int r,int rt)
     2 {
     3     if(l == r)
     4     {
     5         ans[rt] += C;
     6         return ;
     7     }
     8     int mid = (l+r) >> 1;
     9     PushDown(rt,mid-l+1,r-mid);   // 若既有点更新又有区间更新,需要这句话
    10     // 什么叫既有点更新又有区间更新???
    11     if(L <= mid)
    12         Add(L,C,l,mid,rt<<1);
    13     else 
    14         Add(L,C,mid+1,r,rt<<1|1);
    15     PushUp(rt);
    16 }

    (5) 区间更新

     1 void Update(int L,int R,int C,int l,int r,int rt)
     2 {
     3     if(L <= l && r <= R)
     4     {
     5         ans[rt] += C*(r-l+1);
     6         mx[rt] += C;
     7         mn[rt] += C;
     8         lazy[rt] += C;
     9         return ;
    10     }
    11     int mid = (l+r)>>1;
    12     PushDown(rt,mid-l+1,r-mid);
    13     if(L <= mid)
    14         Update(L,R,C,l,mid,rt<<1);
    15     if(R > mid)
    16         Update(L,R,C,mid+1,r,rt<<1|1);
    17     PushUp(rt);
    18 }

    (6) 区间查询

     1 ll Query(int L,int R,int l,int r,int rt)
     2 {
     3     if(L <= l && r <= R)
     4     {
     5         // return mx[rt];
     6         // return mn[rt];
     7         return ans[rt];
     8     }
     9     int mid = (l+r)>>1;
    10     PushDown(rt,mid-l+1,r-mid);   // 若更新只有点更新,不需要这句
    11     ll ANS = 0;
    12     // ll MAX = 0;
    13     // ll MIN = INF;
    14     if(L <= mid)
    15     {
    16         ANS += Query(L,R,l,mid,rt<<1);
    17         // MAX = max(Query(L,R,l,mid,rt<<1),MAX);
    18         // MIN = min(Query(L,R,l,mid,rt<<1),MIN);
    19     }
    20     if(R > mid)
    21     {
    22         ANS += Query(L,R,mid+1,r,rt<<1|1);
    23         // MAX = max(Query(L,R,mid+1,r,rt<<1|1),MAX);
    24         // MIN = min(Query(L,R,mid+1,r,rt<<1|1),MIN);
    25     }
    26     // return MAX;
    27     // return MIN;
    28     return ANS;
    29 }

    (7) 调用函数

    建树
    Build(1,n,1);Build(1,n,1);

    点更新
    Add(L,C,1,n,1);Add(L,C,1,n,1);
    区间修改
    Update(L,R,C,1,n,1);Update(L,R,C,1,n,1);
    区间查询
    llll ANS=Query(L,R,1,n,1);ANS=Query(L,R,1,n,1);

    若只涉及点更新 只需用(1)(2)(4)(6)
    若只涉及区间更新 需用(1)(2)(3)(5)(6)
    若两种更新都有 则在所有向子区间查询或更新前 都需PushDown()

     
  • 相关阅读:
    HDUOJ---Can you solve this equation?
    HDUOJ---A + B Again
    C++知识整理(进制)
    位运算的方法,大结
    位运算的方法,小结
    C++知识整理(在此感谢大牛的整理)
    nyoj---快速查找素数
    HDUOJ----湫湫系列故事——减肥记I
    《道德经》:十条简洁有用的人生建议
    Nginx的几个常用配置和技巧
  • 原文地址:https://www.cnblogs.com/duny31030/p/14304193.html
Copyright © 2020-2023  润新知