声明    :

仅一张图片转载于http://www.cnblogs.com/shuaiwhu/archive/2012/04/22/2464583.html,自己画太麻烦了。。。那个博客的讲解也很好,只是他用了指针的方式来定义线段树,而我用了结构体,并且他讲了线段树的更高级的操作,若对线段树的初级操作不理解,请继续阅读

线段树作为一种十分常用的数据结构,在NOIP、NOI中广泛的出现,所以在这里对线段树进行简单的讲解。

线段树支持对一个数列的求和、单点修改、求最值(最大、最小)、区间修改(需要lazy标记,暂不讲解)。这几种操作,时间复杂度是(logn)级别的,是一种十分优秀的数据结构。因此其获得了广泛的应用。

定义:顾名思义,它是一种树形结构,但每段不是平常所学的一个点一个点的树,而是一条一条的线段,每条线段包含着一些值,其中最主要的是起始和结束点记作 l,r 即左端点和右端点。

那么该如何划分线段树呢?我们采用二分的思想,即每次将一段取半,再进行接下来的操作,这样综合了操作的方便程度和时间复杂度。因为线段树通过二分得来,所以线段树是一颗二叉树。这也方便了对儿子查找。下面是线段树的图,有利于理解:

建树:仅仅知道模型还是不够的,建树的过程是线段树的关键(build(1,1,n))从一号开始,左端是1,右端是n

位运算 i<<1 等效于 i/2  (i<<1)|1 等效于  i/2+1  加速。。。

inline void update(int i)更新i节点维护的值(求和,最大……)
{
node[i].sum=node[i<<].sum+node[(i<<)|].sum;
node[i].maxx=max(node[i<<].maxx,node[(i<<)|].maxx);
} inline void build(int i,int l,int r)//inline 还是加速
{
node[i].l=l;node[i].r=r;//左右端点为当前递归到的 l 和 r
if(l==r){//若l==r 则当前的树节点是真正意义上的点
node[i].maxx=a[l];//最大值就是本身的值
node[i].sum=a[l];//区间的和就是本身的值
return;
}
int mid=(l+r)/;//因为是二叉树所以以中点为分割点
build(i<<,l,mid);//根据二叉树的知识,左儿子是i/2右儿子是i/2+1
build((i<<)|,mid+,r);
update(i);
}

数列求和:这是线段树的一个典型算法,其他的很多应用都是从中转化的。

为了求和我们定义一个函数sum(int i,int l,int r) i 是开始的树节点,我们默认为1。l 是区间的开始点,它的标号是在数列中的标号,r 是结束点其余同 l。帖下代码:

inline int sum(int i,int l,int r)//inline 又是加速
{
if(node[i].l==l&&node[i].r==r)
return node[i].sum;//若树节点的左右区间与查找区间相同,返回其维护的sum
int mid=(node[i].l+node[i].r)/;//确定该树节点的中点以确定继续查找左儿子还是右儿子
if(r<=mid) return sum(i<<,l,r);//若查找区间最右端小于中点,则该区间完全包含于左儿子中
else if(l>mid) return sum((i<<)|,l,r);//最左端大于中点,查找右儿子
else return sum(i<<,l,mid)+sum((i<<)|,mid+,r)
//若跨越中点,查找左儿子 l 到 mid ,和右儿子的 mid+1 到 r 并返回值
}

区间求最值和区间求和大致相同,自己看一下

inline int Max(int i,int l,int r)
{
if(node[i].l==l&&node[i].r==r)
return node[i].maxx;
int mid=(node[i].l+node[i].r)/;
if(r<=mid) return Max(i<<,l,r);
else if(l>mid) return Max((i<<)|,l,r);
else return max(Max(i<<,l,mid),Max((i<<)|,mid+,r));
}

单点更新:和区间不同,但基本思想还是一样的。

inline void add(int i,int k,int v)//当前计算到的点为i,把数列中的第k个元素加v
{
if(node[i].l==k&&node[i].r==k){//因为更改的单点,所以左右端点均和k相等
node[i].sum+=v;
node[i].maxx+=v;
return;
}
int mid=(node[i].l+node[i].r)/;
if(k<=mid) add(i<<,k,v);//若k小于mid则k在树节点i的左子树中
else add((i<<)|,k,v);//反之
update(i);//更新
}

最后贴下全部的代码基本可以做模板了。。

#include<iostream>
#include<cstdio>
using namespace std;
struct tree{
int l,r,sum,maxx;
};
tree node[];
int n,m,a[];
inline void update(int i)
{
node[i].sum=node[i<<].sum+node[(i<<)|].sum;
node[i].maxx=max(node[i<<].maxx,node[(i<<)|].maxx);
} inline void build(int i,int l,int r)
{
node[i].l=l;node[i].r=r;
if(l==r){
node[i].maxx=a[l];
node[i].sum=a[l];
return;
}
int mid=(l+r)/;
build(i<<,l,mid);
build((i<<)|,mid+,r);
update(i);
} inline void add(int i,int k,int v)
{
if(node[i].l==k&&node[i].r==k){
node[i].sum+=v;
node[i].maxx+=v;
return;
}
int mid=(node[i].l+node[i].r)/;
if(k<=mid) add(i<<,k,v);
else add((i<<)|,k,v);
update(i);
} inline int sum(int i,int l,int r)
{
if(node[i].l==l&&node[i].r==r)
return node[i].sum;
int mid=(node[i].l+node[i].r)/;
if(r<=mid) return sum(i<<,l,r);
else if(l>mid) return sum((i<<)|,l,r);
else return sum(i<<,l,mid)+sum((i<<)|,mid+,r);
} inline int Max(int i,int l,int r)
{
if(node[i].l==l&&node[i].r==r)
return node[i].maxx;
int mid=(node[i].l+node[i].r)/;
if(r<=mid) return Max(i<<,l,r);
else if(l>mid) return Max((i<<)|,l,r);
else return max(Max(i<<,l,mid),Max((i<<)|,mid+,r));
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
scanf("%d",&a[i]);
build(,,n);
for(int i=;i<=m;i++){
int c,a,b;
scanf("%d%d%d",&c,&a,&b);
if(c==) printf("%d\n",sum(,a,b));
else if(c==) add(,a,b);
else if(c==) printf("%d\n",Max(,a,b));
}
}

线段树讲解(数据结构、C++)的更多相关文章

  1. 线段树 B数据结构 牛客练习赛28

    链接:https://ac.nowcoder.com/acm/contest/200/B来源:牛客网 题目描述 qn姐姐最好了~     qn姐姐给你了一个长度为n的序列还有m次操作让你玩,     ...

  2. 【数据结构】线段树(Segment Tree)

    假设我们现在拿到了一个非常大的数组,对于这个数组里面的数字要反复不断地做两个操作. 1.(query)随机在这个数组中选一个区间,求出这个区间所有数的和. 2.(update)不断地随机修改这个数组中 ...

  3. ACMer不得不会的线段树,究竟是种怎样的数据结构?

    大家好,欢迎阅读周三算法数据结构专题,今天我们来聊聊一个新的数据结构,叫做线段树. 线段树这个数据结构很多人可能会有点蒙,觉得没有听说过,但是它非常非常有名,尤其是在竞赛圈,可以说是竞赛圈的必备技能. ...

  4. uestc 1073 秋实大哥与线段树 Label:线段树

    秋实大哥与线段树 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) “学习本无底, ...

  5. A线段树

    线段树专题 顾琪坤 1.简介: 打acm的时候,经常会碰到一类问题,比方给你n个数的序列,然后动态的更改某些数的值,然后又动态地询问某个区间的值的和或者其它乱七八糟的东西,对于单个更改或者询问,也许很 ...

  6. UESTC_秋实大哥与线段树 2015 UESTC Training for Data Structures<Problem M>

    M - 秋实大哥与线段树 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Sub ...

  7. BZOJ 4025: 二分图 [线段树CDQ分治 并查集]

    4025: 二分图 题意:加入边,删除边,查询当前图是否为二分图 本来想练lct,然后发现了线段树分治的做法,感觉好厉害. lct做法的核心就是维护删除时间的最大生成树 首先口胡一个分块做法,和hno ...

  8. poj_3468 线段树

    题目大意 一个数列,每次操作可以是将某区间数字都加上一个相同的整数,也可以是询问一个区间中所有数字的和.(这里区间指的是数列中连续的若干个数)对每次询问给出结果. 思路 对于区间的查找更新操作,可以考 ...

  9. 普及向 ZKW线段树!

    啊,是否疲倦了现在的线段树 太弱,还递归! 那我们就欢乐的学习另外一种神奇的线段树吧!(雾 他叫做zkw线段树   这个数据结构灰常好写(虽然线段树本身也特别好写……) 速度快(貌似只在单点更新方面比 ...

随机推荐

  1. 2014 Web开发趋势

    本文翻译自:http://www.pixelstech.net/article/1401629232-Web-design-trends-for-2014 如今,已然到了Web横行的时代.越来越多的资 ...

  2. 填充Z形二维数组

    形如  1   3 4 10  2  5 9 11  6  8 12 15  7 13 14 16 的数组称谓Z形二维数组.填充这样的数组其实只要按照Z形进行行走填充即可,设置一个flag指示方向,行 ...

  3. HDU 2254 奥运(数论+矩阵)

    题目中文的不解释啊. .. 须要注意的就是:离散数学中,有向图的邻接矩阵A表示全部点之间路径长度为1的路径数量,A^n则表示路径长度为n的路径数量.故须要求某两点在(A^t1)~(A^t2)的路径数量 ...

  4. JavaSE思维导图(七)

  5. 七种Prolog解释器/编译器

    http://blog.sina.com.cn/s/blog_494e45fe0100lh1v.html PROLOG 人工智能领域常用的语言,开发自然语言分析,专家系统,以及所有和智能有关的程序,都 ...

  6. javascript中this指针的认识

    javascript中上下文环境就是this指针,即被调用函数所处的环境.这个上下文环境在大多数情况下指的是函数运行时封装这个函数的那个对象:当不通过任何对象单独调用一个函数时,上下文环境指的就是全局 ...

  7. 智能电视TV开发---直播视频客户端结构设计和实现

    在智能电视TV开发---客户端和服务器通信里面我们实现了客户端和服务端的简单通信,接下来我们做一个简单的客户端界面,来实现手机端来操控智能电视的TV端. 一.存储视频的结构设计 我们在做客户端的时候, ...

  8. javascript笔记—面向对象

    什么是对象: 对象是一个整体,对外提供一些操作. 什么是面向对象: 使用对象时,只关注对象提供的功能,不关注其内部细节,例如jquery 面向对象是一种通用思想,并非只有编程中能用,任何事情都可以用. ...

  9. codeforces 630P. Area of a Star

    题目链接 圆上n个点等距离分布, 求构成的星星的面积. 我们可以求三角形OAB的面积, ∠CAE = 1/2 ∠ COE = PI/n, 那么∠CAO = PI/2n, ∠AOB非常好求, 就是PI/ ...

  10. poj1637 Sightseeing tour 混合图欧拉回路判定

    传送门 第一次做这种题, 尽管ac了但是完全不知道为什么这么做. 题目就是给一些边, 有向边与无向边混合, 问你是否存在欧拉回路. 做法是先对每个点求入度和出度, 如果一条边是无向边, 就随便指定一个 ...