首页 > 其他分享 >线段树

线段树

时间:2023-05-13 20:26:00浏览次数:35  
标签:lazy right int 线段 tree mid left

线段树--解决区间问题的数据结构,相比于树状数组,更具有普适性;

完全二叉树的性质:根节下标为1,,节点为 i 的节点,左子节点为2*i,右子节点为2*i+1;

代表nums中单个元素的节点tree[x]应当在树的最底层,即叶子节点;更大的区间从叶子节点开始向上构成;

代表区间【L,R】的节点 tree【i】,左子节点tree【2*i】表示区间【L,(L+R)/2】的区间和;右子节点tree【2*i+1】表示区间 【(L+R)/2 +1, R】的区间和;

初始化tree数组的大小 总是 令 其 为 4*n;类似于归并排序、快排的分治算法,将原问题不断的划分为左右子问题;

代码摘自:线段树从入门到急停 - 力扣(LeetCode)非常详细;

 核心:一个就是注意单点修改时,递归结束条件的判断,是查询单点的话,就是 左右边界相等终止;查询区间和的话, 判断当前区间是否落在 所求范围内,是的话就加上这个区间;

另一个就是拓展:区间和数组可以替换为区间最值问题;求区间最大值、区间最小值等;

再一个就是区间修改问题,有两种,一种是增量式修改,因为注意到我们的区间和,若不关注每个具体的值,只是区间和的大小,我们无需每次都向下更新到叶子节点,否则会达到O(N)时间复杂度;因此 需要增加一个 数组,判断当前增量是否已经向下更新,我们称之为懒惰标记;; 第二种就是 覆盖式修改:将区间的值都修改为同一值,此时,我们不能依据增量式修改的方法,因为可能修改为0,而向下更新的标记 也是检查是否为0,从而 会产生冲突,所以另起一个数组,判断是否已经向下覆盖;;

代码实现:(此处将 两种修改方法写在一起了,建议分开写)

#include <bits/stdc++.h>
using namespace std;

class Segement_tree{
private:
    vector<int>nums;
    vector<int>tree;
    vector<int>lazy;
    vector<int>is_updated;
    int n;
    void pushUp(int i){
        tree[i] = tree[2*i] + tree[2*i + 1];
    }
    void build(int left, int right, int i){
        if(left == right){
            tree[i] = nums[left];
        }
        int mid = (left + right) / 2;
        build(left, mid, 2*i);
        build(mid + 1, right, 2*i + 1);
        pushUp(i);
    }
    /*单点修改*/
    void add(int index, int x, int left, int right, int i){
        if(left == right){
            tree[i] += x;
            return;
        }
        int mid = (left + right)/2;
        if(index <= mid){
            add(index,x,left,mid,2*i);
        }else{
            add(index,x,mid+1,right,2*i+1);
        }
        pushUp(i);
    }
    void update(int index,int x,int left, int right,int i){
        if(left == right){
            tree[i] = x;
            return;
        }
        int mid = (left + right) /2;
        if(index <= mid){
            update(index,x,left,mid,2*i);
        }else{
            update(index,x,mid+1,right,2*i+1);
        }
        pushUp(i);
    }
    int query(int index,int left,int right, int i){
        if(left == right) return tree[i];
        int mid = (left + right) /2;
        if(index <= mid){
            return query(index,left,mid,2*i);
        }else{
            return query(index,mid+1,right,2*i+1);
        }
    }
    /*区间求和*/
    int sum(int left, int right, int s, int t, int i){
        if(left <= s && t <= right) return tree[i];
        int mid = (s + t) /2;
        int res = 0;
        if(left <= mid){
            res += sum(left,right, s, mid, 2*i);
        }
        if(right > mid){
            res += sum(left,right, mid+1, t, 2*i+1);
        }
        return res;
    }
    /*区间修改: 增量式 */
    void add(int left, int right, int x,int s, int t, int i){
        if(left <= s && t <= right){
            tree[i] += (t-s+1)*x;
            if(s != t) lazy[i] += x;
            return;
        }
        int mid = (s + t)/2;
        if(lazy[i] != 0) pushDown(s,mid,t,i);
        if(left <= mid) add(left,right,x,s,mid,2*i);
        if(right > mid) add(left,right,x,mid+1,t,2*i+1);
        pushUp(i);
    }
    void pushDown(int s,int mid, int t,int i){
        tree[2*i] += (mid-s+1)*lazy[i];
        lazy[2*i] += lazy[i];
        tree[2*i+1] += (t-mid)*lazy[i];
        lazy[2*i+1] += lazy[i];
        lazy[i] = 0;
    }
    /*区间修改: 覆盖式 */
    void update(int left, int right,int x,int s,int t,int i){
        if(left <= s && t <= right){
            tree[i] = (t-s+1)*x;
            if(s != t){
                lazy[i] = x;
                is_updated[i] = true;//未推送
            }
            return;
        }
        int mid = (s+t)/2;
        if(is_updated[i]) pushDown1(s,mid,t,i);
        if(left <= mid) update(left,right,x,s,mid,2*i);
        if(right > mid) update(left,right,x,mid+1,t,2*i+1);
        pushUp(i);
    }
    void pushDown1(int s,int mid,int t,int i){
        tree[2*i] = (mid - s + 1)* lazy[i];
        lazy[2*i] = lazy[i];
        is_updated[2*i] = true;
        tree[2*i+1] = (t - mid) * lazy[i];
        lazy[2*i+1] = lazy[i];
        is_updated[2*i+1] = true;
        is_updated[i] = false;
        lazy[i] = 0;
    }
public:
    Segement_tree(vector<int>& nums){
        this->n = nums.size();
        this->nums = nums;
        this->tree.resize(4*n, 0);
        this->lazy.resize(4*n, 0);
        this->is_updated.resize(4*n, 0);
        build(0, n-1, 1);
    }
    /*单点修改*/
    void add(int index, int x){
        add(index,x,0,n-1,1);
    }
    void update(int index,int x){
        update(index,x,0,n-1,1);
    }
    int query(int index){
        return query(index, 0, n-1,1);
    }
    /*区间求和*/
    int sum(int left,int right){
        return sum(left,right,0, n-1,1);
    }

    /*区间修改 : 增量式*/
    void add(int left,int right,int x){
        add(left,right,x,0,n-1,1);
    }
    /*区间修改: 覆盖式 */
    void update(int left,int right, int x){
        update(left,right,x,0,n-1,1);
    }
};

int main(){

    system("pause");
    return 0;
}

 

标签:lazy,right,int,线段,tree,mid,left
From: https://www.cnblogs.com/xuan01/p/17397312.html

相关文章

  • 「学习笔记」可持久化线段树
    可持久化数据结构(Persistentdatastructure)总是可以保留每一个历史版本,并且支持操作的不可变特性(immutable)。主席树全称是可持久化权值线段树,给定\(n\)个整数构成的序列\(a\),将对于指定的闭区间\(\left[l,r\right]\)查询其区间内的第\(k\)小值。可持久化线段......
  • Chemistry Experiment Codeforces Round 247 (Div. 2) 线段树动态开点,二分
    第一次写的时候还不会线段树的动态开点,写了一个是线段树但是是\(O(N^2)\)的写法,现在用动态开点武装了自己,会了正解\(O(qlogn^2)\)。首先建立一个权值线段树,但这里的权值很大,通过动态开点去建树来节省空间,对于两种操作:操作1,常见的动态开点的单点修改操作2,二分答案,然后在线段树......
  • 吉老师线段树学习笔记(内含吉老师ppt)
    Segmenttreebeats吉老师线段树SegmenttreeBeats!.pdf_免费高速下载|百度网盘-分享无限制(baidu.com)为广大oier们提供学习ppt(笑)历史最大值未完工作用用于维护区间最值和区间历史最值的线段树区间最值引入问题给定一个长度为n的数列A,有m次操作:1.将区间\([l,r]\)里......
  • 线段树合并/分裂
    你说的对,但是你理应会动态开点线段树是什么东西。合并很简单,两棵线段树一块搜,然后逐个节点合并。分裂的话可以按照FHQTreap的方法。假如我们将前\(k\)小和后边分开成\(x,y\),首先看左子树,如果比\(k\)大那右子树给\(y\),递归左子树,反之左子树给\(x\),递归右子树。真没啥......
  • 区间不同数的个数 二维数点 扫描线 可持久化线段树
    二维数点,对于询问的\([l,r]\)区间我们只需要统计有多少个数上一次出现的位置\(pos\)满足\(pos\leql\),即可。template<classT>structBIT{Tc[N];intsize;voidresize(ints){size=s;}Tquery(intx){//1...xassert(x<=size);......
  • 权值线段树模板
    【模板】普通平衡树//AConemoretimes#include<bits/stdc++.h>usingnamespacestd;#definefifirst#definesesecond#definepbpush_back#defineendl'\n'#defineall(x)(x).begin(),(x).end()typedefpair<int,int>pii;constint......
  • 线段树
     1.基础算法1.1快读快写template<typenameT>inlinevoidread(T&t){​ intf=0,c=getchar();t=0;​ while(!isdigit(c))f|=c=='-',c=getchar();​ while(isdigit(c))t=t*10+c-48,c=getchar();​ if(f)t=-t;​}​​templ......
  • 线段树的动态开点模板
    学习自数据结构学习笔记(5)动态开点线段树动态开点线段树感谢大佬们博客的帮助//AConemoretimes#include<bits/stdc++.h>usingnamespacestd;#definefifirst#definesesecond#definepbpush_back#defineendl'\n'#defineall(x)(x).begin(),(x).end()......
  • CF960F Pathwalks | 线段树优化DP
    题目设\(dp[x,w]\)为以结点\(x\)为结尾,且最后一条边边权为\(w\)的最长路径长度。考虑根据顺序加边,对于边\((u,v)\),更新\[dp[v,w]=\max_{w'<w}\{dp[u,w']\}+1\]对于每个节点,建一棵线段树,维护\(dp[x]\),这样每次更新\(dp[v,w]\)就相当于在\(dp[u]\)所对应的线段树中查询\([......
  • 线段树
    1#include<iostream>2#include<string>3#definelllonglong4constintN=1e5+5;56usingnamespacestd;78lltree[N<<2];//线段树,可以是对应的结构体9lllz[N<<2];//延迟标记,也可以是结构体1011//lz标记下传12voidpush_down(intid......