zoukankan      html  css  js  c++  java
  • P3372 线段树模版1

    借着题解系统得梳理一下对于线段树的理解

    先建立一个结构体为下面代码实现打基础

    struct tree{
        int l,r;//存储结点管理的区间范围
        long long pre,add;//pre代表区间权值,add为懒标记,下文会进行讲解
    }t[1000005];

    首先,什么是线段树呢?线段树属于完全二叉树,其中每一个子节点而言,都表示整个序列中的一段子区间。由第一层结点储存单个元素,每个子节点不断向自己的父亲节点传递信息,而父节点存储的信息则是他的每一个子节点信息的整合。

    用数组建立这种数据结构(建树)可以通过如下的递归函数来实现:

    void bulid(int p,int l,int r)
        {
        t[p].l=l;t[p].r=r;//以p为编号的节点维护的区间为l到r
        if(l==r){//l=r的话,这个区间就只有一个数,直接让区间维护的值等于a[i]
            t[p].pre=a[l];
            return;
        }//否则值等于左结点加右结点 
        int mid=l+r>>1;
        bulid(p*2,l,mid);
        bulid(p*2+1,mid+1,r);
        t[p].pre=t[p*2].pre+t[p*2+1].pre;
    } 

    那么,当数据结构已经建成,我们怎样对其中的数据进行修改和查询呢?

    这里要借助一种工具:懒标记。懒标记的作用是记录每次、每个节点要更新的值。由于每个父节点下面都连着子节点,父节点和子节点需要同时实现值的更新。因此,懒标记从最初的父节点开始表示,在父节点值更新后,把子节点的懒标记更新为父节点懒标记,父节点本身懒标记为0。光说可能难以理解,请看代码:

    void spread(int p){
        if(t[p].add){//如果懒标记不为0,修改左右结点的值
            t[p*2].pre+=t[p].add*(t[p*2].r-t[p*2].l+1);
            t[p*2+1].pre+=t[p].add*(t[p*2+1].r-t[p*2+1].l+1);
            t[p*2].add+=t[p].add;//该节点的左右结点打上标记
            t[p*2+1].add+=t[p].add;
            t[p].add=0;//将该节点的懒标记清0
        }
    }

    现在我们就可以愉快地进行数据修改了,从根节点开始搜索,如果某个结点表示的区间完全包含于要修改的值的范围,那么对该结点的值进行上述的懒标记更新。

    void change(int p,int x,int y,int z){
        if(x<=t[p].l && y>=t[p].r)//被覆盖的话,就对其进行修改
        {
            t[p].pre+=(long long)z*(t[p].r-t[p].l+1);
            t[p].add+=z;//打上懒标记
            return;
        }
        spread(p);//如果发现没有被覆盖,那就需要继续向下找,将懒标记下放
        int mid=t[p].l+t[p].r>>1;
        if(x<=mid) change(p*2,x,y,z);//如果要修改的区间覆盖了左结点,就修改左结点 
        if(y>mid) change(p*2+1,x,y,z);//右结点同理
        t[p].pre=t[p*2].pre+t[p*2+1].pre;//最终的值等于左结点的值+右结点的值   
    }

    到此为止,我们只剩下值的查询一个问题,但不用想都能发现,查询和修改根本没有什么区别,通过懒标记同理实现即可。

    long long ask(int p,int x,int y)
    {
        if(x<=t[p].l && y>=t[p].r) return t[p].pre;//如果被覆盖,就返回值
        spread(p);//使用懒标记,查询左右结点
        int mid=t[p].l+t[p].r>>1;
        long long ans=0;
        if(x<=mid) ans+=ask(p*2,x,y);
        if(y>mid) ans+=ask(p*2+1,x,y);//累加答案,返回左右结点值的和
        return ans;
    }

    穿在一起,就构成了线段树的基本操作了

  • 相关阅读:
    springmvc
    POJ 3683 Priest John's Busiest Day
    POJ 3678 Katu Puzzle
    HDU 1815 Building roads
    CDOJ UESTC 1220 The Battle of Guandu
    HDU 3715 Go Deeper
    HDU 3622 Bomb Game
    POJ 3207 Ikki's Story IV
    POJ 3648 Wedding
    HDU 1814 Peaceful Commission
  • 原文地址:https://www.cnblogs.com/charlesss/p/10296701.html
Copyright © 2011-2022 走看看