线段树之——区间修改区间查询

1.概述

线段树,也叫区间树,是一个完全二叉树,它在各个节点保存一条线段(即“子数组”),因而常用于解决数列维护问题,基本能保证每个操作的复杂度为O(lgN)。

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。

对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。

使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。

2.线段树基本操作

线段树的基本操作主要包括构造线段树,区间查询和区间修改。

(1)线段树构造

首先介绍构造线段树的方法:让根节点表示区间[0,N-1],即所有N个数所组成的一个区间,然后,把区间分成两半,分别由左右子树表示。不难证明,这样的线段树的节点数只有2N-1个,是O(N)级别的,如图:

节点定义如下:

typedef struct node {
int l; //线段的左端点
int r; //线段的左端点
int value; //线段上的值
}node;

线段树的构建:

  • 伪代码

    bulid//以节点v为根建树、v对应区间为[l,r]
    {
    对节点v初始化
    if (l!=r) {
    以v的左孩子为根建树,区间为[l,(l+r)/]
    以v的右孩子为根建树,区间为[(l+r)/+,r]
    }
    }
  • 完整的建树代码

    #define N 10000
    node tree[N];
    void bulid(int l, int r, int v) //对结点v进行建立,区间为l~r
    {
    tree[v].l = l;
    tree[v].r = r;
    if(l == r) {
    //进行结点的初始化
    tree[v].value = a[r];
    return;
    }
    int mid = (l + r) / ;
    bulid(v * , l, mid);
    bulid(v * + , mid + , r);
    //根据左右儿子更新当前结点
    tree[v].value = tree[v * ].value + tree[v * + ].value;
    }

题目实现:

  • 更新

    当在a[i]~a[j]上的所有的元素都加上一个值c的时候

    如果a[i]~a[j]刚还是一个完整段的时候,直接将这个段的value值加上c*(r-l+1)

    当更新的区间不是一个完整段的时候,采用一种记录增量的方法:给每个节点增加一个域:int add,记录更新操作的增量c,初始的时候add均为0,比如当对2~5区间更新后,给该结点的add加上一个值c,再下次要对2~3结点进行更新或查询时,再将add传递到下面的孩子结点中去

    完整的更新树代码如下:

    typedef struct node {
    int l; //线段的左端点
    int r; //线段的左端点
    int value; //线段上的值
    int add;
    }node;
    void update(int v, int r, int l, int m)//更新区间l~r加上数m
    {
    if(tree[v].l == l && tree[v].r == r) { //找到,更新并记录增量
    tree[v].value += m * (r - l + );
    tree[v].add = m;
    return;
    }
    if(tree[v].add) {
    tree[ * v].add += tree[v].add;
    tree[ * v + ].add += tree[v].add;
    tree[v].add = ;
    }
    int mid = (tree[v].l + tree[v].r) / ;
    if(r <= mid) {
    update(v * , l, r, m); //只对左儿子更新
    } else {
    if(l > mid) {
    update(v * + , l, r, m); //只对右儿子更新
    } else { //区间横跨左右儿子区间,对其两者均进行更新
    update(v * , l, mid, m);
    update(v * + , mid + , r, m);
    }
    }
    }
  • 查询

    查询区间l~r上的value值

void query(int v, int l, int r)  //当前查询结点为v,要查询的区间为l~r
{
if(tree[v].l == l && tree[v].r == r) {
ans += tree[v].value;
return;
}
if(tree[v].add) {
tree[v * ].add += tree[v].add;
tree[v * + ].add += tree[v].add;
tree[v].add = ;
}
int mid = (tree[v].l + tree[v].r) / ;
if(r <= mid) {
query(v * , l, r); //要查询的区间都在左儿子
} else {
if(l > mid) {
query(v * + , l, r); //要查询的区间都在左儿子
} else { //要查询的区间横跨左右孩子
query(v * , l, mid);
query(v * + , mid + , r);
}
}
}

注:

  源地址:http://www.cnblogs.com/archimedes/p/segment-tree.html

线段树(segment_tree)的更多相关文章

  1. 并查集 + 线段树 LA 4730 Kingdom

    题目传送门 题意:训练指南P248 分析:第一个操作可以用并查集实现,保存某集合的最小高度和最大高度以及城市个数.运用线段树成端更新来统计一个区间高度的个数,此时高度需要离散化.这题两种数据结构一起使 ...

  2. 线段树(多维+双成段更新) UVA 11992 Fast Matrix Operations

    题目传送门 题意:训练指南P207 分析:因为矩阵不超过20行,所以可以建20条线段的线段树,支持两个区间更新以及区间查询. #include <bits/stdc++.h> using ...

  3. HDU 5405 (树链剖分+线段树)

    Problem Sometimes Naive 题目大意 给你一棵n个节点的树,有点权. 要求支持两种操作: 操作1:更改某个节点的权值. 操作2:给定u,v, 求 Σw[i][j]   i , j ...

  4. BZOJ 3531(树链剖分+线段树)

    Problem 旅行 (BZOJ 3531) 题目大意 给定一颗树,树上的每个点有两个权值(x,y). 要求维护4种操作: 操作1:更改某个点的权值x. 操作2:更改某个点的权值y. 操作3:求a-- ...

  5. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  6. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  7. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  8. 线段树(区间合并) POJ 3667 Hotel

    题目传送门 /* 题意:输入 1 a:询问是不是有连续长度为a的空房间,有的话住进最左边 输入 2 a b:将[a,a+b-1]的房间清空 线段树(区间合并):lsum[]统计从左端点起最长连续空房间 ...

  9. BZOJ_1018_[SHOI2008]_交通堵塞traffic_(线段树)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1018 \(2*n\)的距形,起初没有边相连,之后有三种操作: 1.加边. 2.删边. 3.询问 ...

随机推荐

  1. 【Vagrant】-NO.130.Vagrant.1 -【Vagrant】

    Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...

  2. laravel----------laravel一些注意事项和一些说明

    1.php artisan key:generate 解释:.env文件里面的APP_KEY参数设置为一个随机字符串也就是这个key是一个随机字符串,用于实现框架中的encrypt(加密)服务,   ...

  3. spark MLlib实现的基于朴素贝叶斯(NaiveBayes)的中文文本自动分类

    1.自动文本分类是对大量的非结构化的文字信息(文本文档.网页等)按照给定的分类体系,根据文字信息内容分到指定的类别中去,是一种有指导的学习过程. 分类过程采用基于统计的方法和向量空间模型可以对常见的文 ...

  4. 2017 5 31 php面向过程 面向对象1

    面向过程的语法定义变量:定义函数:使用变量(输出,赋值,等)调用函数:流程控制(if,switch,for,while等) 面向对象的语法1,定义类:定义类的语法中,只有这3种代码:1.1定义属性(变 ...

  5. Python socket的客户端

    做一个socket客户端1.声明一个实例2.绑定端口号和地址3.循环发送和接收响应其中要注意粘包的产生,为了防止粘包的产生,应该在服务器端先测出要发送信息的大小,然后发送响应至客户端,等到服务器上一条 ...

  6. HTTP协议基础总结

    1,HTTP协议协议的概念:协议就是指计算机网络中,两台计算机之间进行通讯所必须共同遵守的规定和规则.HTTP协议:超文本传输协议是一种通信协议,它允许将超文本标记语言(html)文档从web服务器传 ...

  7. 接口自动化测试持续集成--Soapui接口功能测试参数化

    按照自动化测试分层实现的原理,每一层的脚本实现都要进行参数化,自动化的目标就是要实现脚本代码与测试数据分离. 当测试数据进行调整的时候不会对脚本的实现带来震荡,从而提高脚本的稳定性与灵活度,降低脚本的 ...

  8. 【stm32开发之HAL采坑记之:001使用ST-LINK下载出错问题】

    使用ST-LINK下载出错问题 开发环境:swstm32+stm32cubeMx+硬石F1开发板 联系方式:yexiaopeng1992@126.com 出现问题:在使用ST-linik下载时发现,在 ...

  9. 编译jmeter5.0源码

    jmeter5.0使用过程中,遇到request或者response乱码的情况,想要一次性解决这个问题,需要编译ApacheJMeter_http.jar这个包(lib\ext文件下)里的Reques ...

  10. jquery的cookie插件

    一.JS文件 /*! * jQuery Cookie Plugin v1.4.1 * https://github.com/carhartl/jquery-cookie * * Copyright 2 ...