ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

训练日志2:线段树简述

2021-10-02 23:02:31  阅读:146  来源: 互联网

标签:rt lazy val int 线段 tree 更新 简述 日志


线段树概念:

线段树是一种二叉搜索树,其存储的是一个区间的信息,用[L,R]表示从L到R之间存在的信息。

在这里插入图片描述

在这里插入图片描述

线段树的延迟更新:

在结构体中再定义一个对象 lazy,add记录的是此节点所代表的区间所有数被加的值,在我们进行操作时,比如加减,我们就可以利用lazy然后往下修改,而不用修改一次就遍历一次,大大节约时间。
在这里插入图片描述

线段树的基本操作:

建树、单点查询、单点修改、区间查询、区间修改

结点的构建:

const int maxn=1e6;
struct node{
int l, r,lazy,val;
}tree[maxn<<2];//线段树要开数组的4倍

以求和为例

建树:

void build(int l,int r,int rt){
    tree[rt].lazy=0;//初始化lazy标记
    tree[rt].l=l;
    tree[rt].r=r;
    if(l==r){
        tree[rt].val=a[l];
        return;
    }
    else{
        int mid=(l+r)>>1;
        build(l,mid,rt*2);//左孩子
        build(mid,r,rt*2+1);//右孩子
        tree[rt].val=tree[rt*2].val+tree[rt*2+1].val;状态合并,此结点的为两个孩子的val和
    }
}

单点查询

int querynode(int rt){//单点查询(二分)
    if(tree[rt].l==tree[rt].r){//当前结点的左右端点相等,为叶子节点,是最终答案
        return tree[rt].val;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(rt<=mid)//目标位置比中点靠左,就递归左孩子
        querynode(rt*2);
    else//反之,递归右孩子
        querynode(rt*2+1);
}

单点修改

void updatenode(int rt,int c){
    if(tree[rt].l==tree[rt].r){//找到目标位置
        tree[rt].val+=c;
        return;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(rt<=mid)//目标位置比中点靠左,就递归左孩子
        updatenode(rt*2,c);
    else//反之,递归右孩子
        updatenode(rt*2+1,c);
    tree[rt].val=tree[rt*2].val+tree[rt*2+1].val;//所有包含结点k的结点状态更新
}

区间查询:

int query(int l,int r,int rt){//区间查询
    if(tree[rt].l>=l&&tree[rt].r<=r)
        return tree[rt].val;
    else if(l>l||r<1)
        return 0;
    else{
        return query(l,r,rt<<1)+query(l,r,rt<<1|1);
    }
}

区间修改:

为了避免超时,引用延迟标记
每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新 or 询问的时候。下次更新或者查询的时候,如果查到该节点,就把延迟标记进行下传,将值加到他的子节点上去,同时将延迟标记变为 0,避免下次重复更新。这样只更新到查询的子区间,不需要再往下找了,极大的降低了时间复杂度。
在这里插入图片描述
下推:

void pushdown(int rt){//下推
    tree[rt*2].lazy+=tree[rt].lazy;//左孩子更新延迟标记
    tree[rt*2+1].lazy+=tree[rt].lazy;//右孩子更新延迟标记
    tree[rt*2].val+=tree[rt].lazy*(tree[rt*2].r-tree[rt*2].l+1);//左孩子状态更新
    tree[rt*2+1].val+=tree[rt].lazy*(tree[rt*2+1].r-tree[rt*2+1].l+1);//右孩子状态更新
    tree[rt].lazy=0;//清除本结点标记
}

区间修改:

void updata(int l,int r,int rt,int c){//区间更新
    if(tree[rt].l>=l&&tree[rt].r<=r){
        tree[rt].val+=c*(tree[rt].r-tree[rt].l+1);
        tree[rt].lazy+=c;
        return;
    }
    pushdown(rt);
    updata(l,r,rt*2,c);
    updata(l,r,rt*2+1,c);
    tree[rt].val=tree[rt*2].val+tree[rt*2+1].val;

}

明天整理题目

标签:rt,lazy,val,int,线段,tree,更新,简述,日志
来源: https://blog.csdn.net/qq_52237775/article/details/120581111

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有