首页 > 其他分享 >数据结构:线段树基础详解

数据结构:线段树基础详解

时间:2022-10-25 14:37:24浏览次数:87  
标签:标记 int 线段 mid 详解 区间 数据结构 sum

1.简介

线段树,顾名思义,就是由线段构成的树,是一个较为优秀的数据结构,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点,通常用于解决区间类的问题,在各大OI赛事中频繁出现。下面我将为你展示线段树的一些基本操作及原理

2.存储

线段树一般用结构体存储,代码如下:

struct node{
    int l,r,num,add;
}tree[10010];
//add 用于懒标记

 3.建树

代码如下:

void buildtree(int x,int y,int p){
    t[p].l = x,t[p].r = y;
    if (x == y){
        t[p].sum = a[x];
        return;
    }
    int mid = x+y>>1;
    buildtree(x,mid,p<<1);
    buildtree(mid+1,y,(p<<1)+1);
    t[p].sum = t[p<<1].sum+t[(p<<1)+1].sum;
}

4.懒标记

从前有一个人,太懒了,修改线段树区间值时不想把线段树全都遍历一遍,于是就有了懒标记

懒标记的精髓就是打标记和下传操作,由于我们要做的操作是区间加一个数,所以我们不妨在区间进行修改时为该区间打上一个标记,就不必再修改他的儿子所维护区间,等到要使用该节点的儿子节点维护的值时,再将懒标记下放即可,可以节省很多时间,对于每次区间修改和查询,将懒标记下传,可以节省很多时间。当然,这一操作不是必要的,在不住求代码运行速度的时候可以不用

代码如下:

void tag(int p){
    if (t[p].add){ //如果懒标记不为空,则进行下传操作 
        t[p<<1].sum += t[p].add*(t[p<<1].r-t[p<<1].l+1); //这里,因为luogu的模板题中修改是对于区间内每一个值的所以是乘
        t[(p<<1)+1].sum += t[p].add*(t[(p<<1)+1].r-t[(p<<1)+1].l+1);
        t[p<<1].add += t[p].add;
        t[(p<<1)+1].add += t[p].add;
        t[p].add = 0;
    }
}

5.区间修改

从根节点自上而下查找,当发现有区间覆盖要修改的节点时,我们就把这一区间修改并打上懒标记。否则下传懒标记,继续查找。

代码如下:

void change(int p,int x,int y,int z){
    if (x<=t[p].l&&y>=t[p].r){
        t[p].sum+=(ll)z*(t[p].r-t[p].l+1);
        t[p].add+=z;
        return;
    }
    tag(p);
    int mid = t[p].l+t[p].r>>1;
    if (x<=mid){
        change(p<<1,x,y,z);
    } //左儿子包含于修改区间内 
    if (y>mid){ //用if 不用else if 
        change((p<<1)+1,x,y,z);
    } //右儿子包含于修改区间内
    t[p].sum = t[p<<1].sum+t[(p<<1)+1].sum; 
}

6.区间查询

考虑询问一个区间的和,依旧是从根节点向下查找,当发现该节点被覆盖时,就返回维护的值,否则下传懒标记,查询左右儿子,累加答案

代码如下:

long long ask(int p,int x,int y){
    if (x<=t[p].l&&y>=t[p].r){
        return t[p].sum;
    }
    tag(p);
    int mid = t[p].l+t[p].r>>1;
    long long ans = 0;
    if (x<=mid) ans+=ask(p<<1,x,y);
    if (y>mid) ans+=ask((p<<1)+1,x,y);
    return ans;
}

7.完整代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[101010];
struct node{
    ll l,r,sum,add;
}t[401010];
void buildtree(int x,int y,int p){
    t[p].l = x,t[p].r = y;
    if (x == y){
        t[p].sum = a[x];
        return;
    }
    int mid = x+y>>1;
    buildtree(x,mid,p<<1);
    buildtree(mid+1,y,(p<<1)+1);
    t[p].sum = t[p<<1].sum+t[(p<<1)+1].sum;
}
void tag(int p){
    if (t[p].add){ //如果懒标记不为空,则进行下传操作 
        t[p<<1].sum += t[p].add*(t[p<<1].r-t[p<<1].l+1);
        t[(p<<1)+1].sum += t[p].add*(t[(p<<1)+1].r-t[(p<<1)+1].l+1);
        t[p<<1].add += t[p].add;
        t[(p<<1)+1].add += t[p].add;
        t[p].add = 0;
    }
}
void change(int p,int x,int y,int z){
    if (x<=t[p].l&&y>=t[p].r){
        t[p].sum+=(ll)z*(t[p].r-t[p].l+1);
        t[p].add+=z;
        return;
    }
    tag(p);
    int mid = t[p].l+t[p].r>>1;
    if (x<=mid){
        change(p<<1,x,y,z);
    } //左儿子包含于修改区间内 
    if (y>mid){ //用if 不用else if 
        change((p<<1)+1,x,y,z);
    } //右儿子包含于修改区间内
    t[p].sum = t[p<<1].sum+t[(p<<1)+1].sum; 
}
ll ask(int p,int x,int y){
    if (x<=t[p].l&&y>=t[p].r){
        return t[p].sum;
    }
    tag(p);
    int mid = t[p].l+t[p].r>>1;
    ll ans = 0;
    if (x<=mid) ans+=ask(p<<1,x,y);
    if (y>mid) ans+=ask((p<<1)+1,x,y);
    return ans;
}
int main(){
    int n,m;
    cin>>n>>m;
    for (int i=1;i<=n;i++){
        cin>>a[i];
    }
    buildtree(1,n,1);
    int k,c,s,p;
    for (int i=1;i<=m;i++){
        cin>>k;
        if (k == 1){
            cin>>c>>s>>p;
            change(1,c,s,p);
        }
        else if (k == 2){
            cin>>c>>s;
            cout<<ask(1,c,s)<<endl;
        }
    }
    return 0;
} 

 

Over~

 

 

 

  

 

标签:标记,int,线段,mid,详解,区间,数据结构,sum
From: https://www.cnblogs.com/reasa/p/16824671.html

相关文章

  • 数据结构之栈
     #include<stdio.h>#include<stdlib.h>/////////////////函数结果状态代码/////////////#defineTRUE1#defineFALSE0#defineOK1#defineERROR0#def......
  • WLAN 帧详解
    帧格式如上是一个80211的帧格式,传输顺序是从左向右发送,也就是是说最高bit将会最后出现FrameControlFrameControl如下图所示FrameControl的第一个字段是Protocal目前......
  • 某高校考研数据结构背诵
    第一章      绪论1.       数据元素之间的关系在计算机中有几种表示方式?各有什么特点?a)      顺序存储方式。数据元素顺序存放,每个存储结点只含......
  • 某高校考研数据结构真题
    真题1.      试说明什么是好的散列函数?“好”的散列函数应满足的条件:①均匀分布,少冲突                                 ......
  • 某高校考研数据结构简单题
    散列表若在散列表中删除一个记录,应如何操作?为什么?答:在散列表中删除一个记录:       在拉链法情况下,可以物理地删除。       在开放定址法情况下,不......
  • Redis数据结构(一)-Redis的数据存储及String类型的实现
    1引言Redis作为基于内存的非关系型的K-V数据库。因读写响应快速、原子操作、提供了多种数据类型String、List、Hash、Set、SortedSet、在项目中有着广泛的使用,今天我们来......
  • Flask学习笔记(十五)-Flask 上下文详解
    一、上下文说明上下文:在程序中可以理解为在代码执行到某一时刻时,根据之前代码所做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以完成的事......
  • Redis数据结构(一)-Redis的数据存储及String类型的实现
    1引言Redis作为基于内存的非关系型的K-V数据库。因读写响应快速、原子操作、提供了多种数据类型String、List、Hash、Set、SortedSet、在项目中有着广泛的使用,今天我们......
  • fzu_noip 1039(盖楼-线段树)
    盖楼时限:1s内存:32M★问题描述:S举办了一场盖楼比赛,有n位选手参赛,将这n位选手编号为1到n。比赛刚开始时第i位选手的房子的初始高度为Ai,每过一天该选手的房子高度增加Bi。S想......
  • BZOJ 3110([Zjoi2013]K大数查询-区间第k大[段修改,在线]-树状数组套函数式线段树)
    3110:[Zjoi2013]K大数查询TimeLimit: 20Sec  MemoryLimit: 512MBSubmit: 418  Solved: 235[​​Submit​​][​​Status​​][​​Discuss​​]Des......