• P3372 【模板】线段树 1


    线段树模板题

    这个题的题面十分好理解,不再进行过多解释。

    做题思路(数组)

    建树与维护

    建树代码

    void build(int l, int r, int & rt) {//递归建树
        rt = ++tot;
        tr[rt].l = l, tr[rt].r = r; 
        if (l == r) { 
            scanf("%lld", &tr[rt].v);
            return;
        }
        int mid = (l + r) >> 1; 
        build(l, mid, tr[rt].lc);
        build(mid + 1, r, tr[rt].rc); 
        push_up(tr[rt].lc, tr[rt].rc, rt); //对信息进行维护
    }
    

    维护代码

    void push_up(int lc, int rc, int rt) { 
        tr[rt].v = tr[lc].v + tr[rc].v;
    }
    

    区间修改实现

    void push_down(int lc, int rc, int rt) { 
        tr[lc].tag += tr[rt].tag; 
        tr[rc].tag += tr[rt].tag;
        tr[lc].v += (tr[lc].r - tr[lc].l + 1) * tr[rt].tag; //修改区间和
        tr[rc].v += (tr[rc].r - tr[rc].l + 1) * tr[rt].tag;
        tr[rt].tag = 0; 
    }
    
    void up_date(int l, int r, int rt) {
        if (x <= l && y >= r) { 
            tr[rt].tag += k; 
            tr[rt].v += (r - l + 1) * k; 
            return;
        }
        push_down(tr[rt].lc, tr[rt].rc, rt); 
        int mid = (l + r) >> 1;
        if (x <= mid)
            up_date(l, mid, tr[rt].lc);
        if (y > mid)
            up_date(mid + 1, r, tr[rt].rc); 
        push_up(tr[rt].lc, tr[rt].rc, rt); 
    }
    
    long long get_ans(int l, int r, int rt) {
        if (x <= l && y >= r) 
            return tr[rt].v; 
        push_down(tr[rt].lc, tr[rt].rc, rt); 
        ll ans = 0;
        int mid = (l + r) >> 1;
        if (x <= mid)
            ans += get_ans(l, mid, tr[rt].lc);
        if (y > mid)
            ans += get_ans(mid + 1, r, tr[rt].rc);
        return ans;
    }
    

    代码实现(数组)

    #include <cstdio>
    typedef long long int ll;
    using namespace std;
    #define N 200100 
    int n, m, root, tot, p, x, y;
    ll k;
    struct node {
        int l, r, lc, rc; 
        long long v, tag; 
    } tr[N];
    
    void push_up(int lc, int rc, int rt) { 
        tr[rt].v = tr[lc].v + tr[rc].v;
    }
    
    void push_down(int lc, int rc, int rt) { 
        tr[lc].tag += tr[rt].tag; 
        tr[rc].tag += tr[rt].tag;
        tr[lc].v += (tr[lc].r - tr[lc].l + 1) * tr[rt].tag; //修改区间和
        tr[rc].v += (tr[rc].r - tr[rc].l + 1) * tr[rt].tag;
        tr[rt].tag = 0; 
    }
    
    void build(int l, int r, int & rt) {
        rt = ++tot;
        tr[rt].l = l, tr[rt].r = r; 
        if (l == r) { 
            scanf("%lld", &tr[rt].v);
            return;
        }
        int mid = (l + r) >> 1; 
        build(l, mid, tr[rt].lc);
        build(mid + 1, r, tr[rt].rc); 
        push_up(tr[rt].lc, tr[rt].rc, rt); 
    }
    
    void up_date(int l, int r, int rt) {
        if (x <= l && y >= r) { 
            tr[rt].tag += k; 
            tr[rt].v += (r - l + 1) * k; 
            return;
        }
        push_down(tr[rt].lc, tr[rt].rc, rt); 
        int mid = (l + r) >> 1;
        if (x <= mid)
            up_date(l, mid, tr[rt].lc);
        if (y > mid)
            up_date(mid + 1, r, tr[rt].rc); 
        push_up(tr[rt].lc, tr[rt].rc, rt); 
    }
    
    ll get_ans(int l, int r, int rt) {
        if (x <= l && y >= r) 
            return tr[rt].v; 
        push_down(tr[rt].lc, tr[rt].rc, rt); 
        ll ans = 0;
        int mid = (l + r) >> 1;
        if (x <= mid)
            ans += get_ans(l, mid, tr[rt].lc);
        if (y > mid)
            ans += get_ans(mid + 1, r, tr[rt].rc);
        return ans;
    }
    
    int main() {
        scanf("%d %d", &n, &m);
        build(1, n, root);
        while (m--) {
            scanf("%d %d %d", &p, &x, &y);
            if (p == 1) {
                scanf("%lld", &k);
                up_date(1, n, 1);
            } else
                printf("%lld
    ", get_ans(1, n, 1));
        }
        return 0;
    }
    

    下面是指针实现

    虽然使用指针来写线段树的人很少,但是个人觉得使用指针写线段树将更加容易理解。

    做题思路(指针)

    定义结构体

    struct Node {
      ll tag, v;//延迟标记,子节点值 
      int l, r;//左端点,右端点 
      Node *ls, *rs;//左孩子、右孩子 
    }
    

    建树

    Node(const int L, const int R) {//构造函数 
        l = L; r = R; //当前结点左右端点 
        if (l == r) {//叶子结点 
          tag = 0;//清空延迟标记 
          v = a[l];//其值等于这个结点对应的值本身 
          ls = rs = NULL;//没有左右孩子结点 
        } else {
          tag = 0;
          int M = (L + R) >> 1;//取中点 
          ls = new Node(L, M);//递归向下一层进行构造 
          rs = new Node(M + 1, R);
          pushup();//值等于左右孩子结点值的和 
        }
      }
    

    维护信息

    inline void pushup() {//上传信息 
        v = ls->v + rs->v;//当前结点值=左结点值+右结点值 
      }
    

    区间修改的实现

      inline void maketag(const ll w) {//给一个结点打延迟标记 
        v += (r - l + 1) * w;//更新当前结点值 
        tag += w;//延迟标记 
      }
      inline void pushdown() {//下传延迟标记 
        if (tag == 0) return;//当前结点没有延迟标记,直接返回 
        ls->maketag(tag);//向左右孩子打标记 
        rs->maketag(tag);
        tag = 0;//清空当前结点延迟标记 
      }
      inline bool InRange(const int L, const int R) { return (L <= l) && (r <= R); }
      //一个结点被完全包含 
      inline bool OutofRange(const int L, const int R) { return  (l > R) || (r < L); }
      //一个结点完全没有重叠(毫不相关) 
      
      void upd(const int L, const int R, const ll w) {//赋值操作 
        if (InRange(L, R)) {//完全包含 
          maketag(w);//先打上延迟标记 
        } else if (!OutofRange(L, R)) {//有重叠部分但没有完全包含 
          pushdown();//下传延迟标记到子节点 
          ls->upd(L, R, w);//递归更改信息 
          rs->upd(L, R, w); 
          pushup();//向上传递信息 
        } 
      }
      
      ll qry(const  int L, const int R) {//询问一段区间的和 
        if (InRange(L, R)) return v;//完全包含直接返回这个结点的值 
        if (OutofRange(L, R)) return 0;//毫不相关,返回0 
        pushdown();//下传 
        return ls->qry(L, R) + rs->qry(L, R);//递归询问 
      }
    

    代码实现(指针)

    #include <cstdio>
    
    const int maxn = 100005;
    
    typedef long long int ll;
    
    int n, q;
    ll a[maxn];//原数组 
    
    struct Node {
      ll tag, v;//延迟标记,子节点值 
      int l, r;//左端点,右端点 
      Node *ls, *rs;//左孩子、右孩子 
      
      inline void maketag(const ll w) {//给一个结点打延迟标记 
        v += (r - l + 1) * w;//更新当前结点值 
        tag += w;//延迟标记 
      }
      inline void pushup() {//上传信息 
        v = ls->v + rs->v;//当前结点值=左结点值+右结点值 
      }
      inline void pushdown() {//下传延迟标记 
        if (tag == 0) return;//当前结点没有延迟标记,直接返回 
        ls->maketag(tag);//向左右孩子打标记 
        rs->maketag(tag);
        tag = 0;//清空当前结点延迟标记 
      }
      
      Node(const int L, const int R) {//构造函数 
        l = L; r = R; //当前结点左右端点 
        if (l == r) {//叶子结点 
          tag = 0;//清空延迟标记 
          v = a[l];//其值等于这个结点对应的值本身 
          ls = rs = NULL;//没有左右孩子结点 
        } else {
          tag = 0;
          int M = (L + R) >> 1;//取中点 
          ls = new Node(L, M);//递归向下一层进行构造 
          rs = new Node(M + 1, R);
          pushup();//值等于左右孩子结点值的和 
        }
      }
      
      // this l, r
      inline bool InRange(const int L, const int R) { return (L <= l) && (r <= R); }
      //一个结点被完全包含 
      inline bool OutofRange(const int L, const int R) { return  (l > R) || (r < L); }
      //一个结点完全没有重叠(毫不相关) 
      
      void upd(const int L, const int R, const ll w) {//赋值操作 
        if (InRange(L, R)) {//完全包含 
          maketag(w);//先打上延迟标记 
        } else if (!OutofRange(L, R)) {//有重叠部分但没有完全包含 
          pushdown();//下传延迟标记到子节点 
          ls->upd(L, R, w);//递归更改信息 
          rs->upd(L, R, w); 
          pushup();//向上传递信息 
        } 
      }
      
      ll qry(const  int L, const int R) {//询问一段区间的和 
        if (InRange(L, R)) return v;//完全包含直接返回这个结点的值 
        if (OutofRange(L, R)) return 0;//毫不相关,返回0 
        pushdown();//下传 
        return ls->qry(L, R) + rs->qry(L, R);//递归询问 
      }
    };
    
    int main() {
      scanf("%d%d", &n, &q);
      for (int i = 1; i <= n; ++i) scanf("%lld", a + i);
      Node *rot = new Node(1, n);
      for (ll o, x, y, z; q; --q) {
        scanf("%lld%lld%lld", &o, &x, &y);
        if (o == 1) {
          scanf("%lld", &z);
          rot->upd(x, y, z); 
        } else {
          printf("%lld
    ", rot->qry(x, y));
        }
      }
      return 0;
    }
    

    鸣谢

    感谢zay大佬讲解线段树的指针实现,zay Orz !

  • 相关阅读:
    Benelux Algorithm Programming Contest 2016 Preliminary K. Translators’ Dinner(思路)
    Benelux Algorithm Programming Contest 2016 Preliminary Target Practice
    Benelux Algorithm Programming Contest 2016 Preliminary I. Rock Band
    Benelux Algorithm Programming Contest 2016 Preliminary A. Block Game
    ICPC Northeastern European Regional Contest 2019 Apprentice Learning Trajectory
    ICPC Northeastern European Regional Contest 2019 Key Storage
    2018 ACM ICPC Asia Regional
    2018 ACM ICPC Asia Regional
    Mybatis入库出现异常后,如何捕捉异常
    优雅停止 SpringBoot 服务,拒绝 kill -9 暴力停止
  • 原文地址:https://www.cnblogs.com/Kyriech-Francis/p/Answer_P3372.html
Copyright © 2020-2023  润新知