线段树 点更新和段更新

 线段树
标签: acm数据结构
2016-08-18 17:26 82人阅读 评论(0) 收藏 举报
分类:
数据结构(9)

内容来自石万东学长课件~

1.问题
给你一个数的序列A1A2……An。 并且可能多次进行下列两个操作

       1、对序列里面的某个数进行加减

       2、询问这个序列里面任意一个连续的子序列AiAi+1……Aj的和是多少。
希望第2个操作每次能在log(n)时间内完成

2.线段树

注意:

线段树的分解:

        如何从[1,9]分解出[2,8]

分解的规则就是:如果有某个节点代表的区间,完全属于待分解区间,则该节点为“终止”节点,不再继续往下分解
所有“终止”节点所代表的区间都不重叠,且加在一起就恰好等于整个待分解区间


线段树的特征(时间复杂度):

1、线段树的深度不超过log2(n)+1(向上取整,n是根节点对应区间的长度)。

2、线段树上,任意一个区间被分解后得到的“终止节点”数目都是log(n)量级。
线段树能在O(log(n))的时间内完成插入数据,更新数据、查找、统计等工作,提供了理论依据


线段树的题目分类:
可分为以下四大类:
1、单点更新:最最基础的线段树,只更新叶子节点,然后回溯更新其父亲结点
2、成段更新:(通常这对初学者来说是一道坎),需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候

3、区间合并 这类题目会询问区间中满足条件的连续最长区间,所以回溯的时候需要对左右儿子的区间进行合并(hdu 1540)
4、扫描线:这类题目需要将一些操作排序,然后从左到右用一根扫描线(当然是在我们脑子里)扫过去 最典型的就是矩形面积并,周长并等题(poj 1151)


一、点更新:

建树:



    left和reight代表【2,8】的28struct node{  
        int l,r,maxn,sum; //表示最大值和总和  
     }tree[N<<2];  

    void build(int m,int l,int r){  
        tree[m].l = l;  
        tree[m].r = r;  
        if(l == r){  
            tree[m].maxn = a[l];  
            tree[m].sum = a[l];  
            return ;  
        }  
        int mid = (l+r)>>1;  
        build(m<<1,l,mid);  
        build((m<<1)+1,mid+1,r);  
        tree[m].maxn = max(tree[m<<1].maxn,tree[(m<<1)+1].maxn);  
        tree[m].sum = tree[m<<1].sum + tree[(m<<1)+1].sum;  
    }  


更新:



    void update(int m,int a,int val){  
        if(tree[m].l == a && tree[m].r == a){  
            tree[m].maxn = val;//tree[m].maxn += val;  
            tree[m].sum = val;//tree[m].sum += val;  
            return ;  
        }  
        int mid = (tree[m].l+tree[m].r)>>1;  
        if(a <= mid)  
            update(m<<1,a,val);  
        else  
            update((m<<1)+1,a,val);  
        tree[m].maxn = max(tree[m<<1].maxn,tree[(m<<1)+1].maxn);  
        tree[m].sum = tree[m<<1].sum + tree[(m<<1)+1].sum;  
    }  


查询:


    int query_max(int m,int l,int r){  
        if(l == tree[m].l && r == tree[m].r)  
            return tree[m].maxn;  
            //return tree[m].sum;  
        int mid = (tree[m].l+tree[m].r)>>1;  
        if(r <= mid)  
            return query_max(m<<1,l,r);  
        if(l > mid)  
            return query_max((m<<1)+1,l,r);  
        return max(query_max(m<<1,l,mid), query_max((m<<1)+1,mid+1,r));  
        //return query_sum(m<<1,l,mid) + query_sum((m<<1)+1,mid+1,r);  
    }  

    build(1,1,n);  
    update(1,a,b);  
    query(1,a,b);  


二、段更新

延迟标记精讲:点击打开链接



    struct node{  
        ll l,r;  
        ll addv,sum;  
    }tree[maxn<<2];  

    void maintain(int id)  
    {  
        if(tree[id].l >= tree[id].r)  
            return ;  
        tree[id].sum = tree[id<<1].sum + tree[id<<1|1].sum;  
    }  

    void pushdown(int id)  
    {  
        if(tree[id].l >= tree[id].r)  
            return ;  
        if(tree[id].addv){  
            int tmp = tree[id].addv;  
            tree[id<<1].addv += tmp;  
            tree[id<<1|1].addv += tmp;  
            tree[id<<1].sum += (tree[id<<1].r - tree[id<<1].l + 1)*tmp;  
            tree[id<<1|1].sum += (tree[id<<1|1].r - tree[id<<1|1].l + 1)*tmp;  
            tree[id].addv = 0;  
        }  
    }  

    void build(int id,ll l,ll r)  
    {  
        tree[id].l = l;  
        tree[id].r = r;  
        tree[id].addv = 0;  
        tree[id].sum = 0;  
        if(l==r)  
        {  
            tree[id].sum = 0;  
            return ;  
        }  
        ll mid = (l+r)>>1;  
        build(id<<1,l,mid);  
        build(id<<1|1,mid+1,r);  
        maintain(id);  
    }  

    void updateAdd(int id,ll l,ll r,ll val)  
    {  
        if(tree[id].l >= l && tree[id].r <= r)  
        {  
            tree[id].addv += val;  
            tree[id].sum += (tree[id].r - tree[id].l+1)*val;  
            return ;  
        }  
        pushdown(id);  
        ll mid = (tree[id].l+tree[id].r)>>1;  
        if(l <= mid)  
            updateAdd(id<<1,l,r,val);  
        if(mid < r)  
            updateAdd(id<<1|1,l,r,val);  
        maintain(id);  
    }  

    void query(int id,ll l,ll r)  
    {  
        if(tree[id].l >= l && tree[id].r <= r){  
            anssum += tree[id].sum;  
            return ;  
        }  
        pushdown(id);  
        ll mid = (tree[id].l + tree[id].r)>>1;  
        if(l <= mid)  
            query(id<<1,l,r);  
        if(mid < r)  
            query(id<<1|1,l,r);  
        maintain(id);  
    }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值