关于线段树的初步理解

POJ 3264

根据题目意思就是给定一段序列,然后给出几个区间,要求出该段区间中最大值与最小值之差。

首先我想到的是用数组存储这一段序列,然后每次根据区间的左右边界来遍历这个段序列然后找到最大值和最小值,显然这样的方法是最容易想到的,但是可想而知这样的方法会耗费很多的时间,时间复杂度太大。

然后,我进行了第二种思考,就是利用二维数组来存储左边界到右边界的最大值;

(1)如果l==r,那么显然当前区间的最大值和最小值就是l这个位置的数本身

(2)max1[i][j]=max{max[i][j-1],height[j]};

#include <iostream>
#include <algorithm>
using namespace std;
int main(){
    int n,q;
    while(cin>>n>>q){
        int *height=new int[n];
        for(int i=0;i<n;i++){
            cin>>height[i];
        }
        int seta,setb;
        int max1[n][n];
        int min1[n][n];
        for(int i=0;i<n;i++){
            max1[i][i]=height[i];
            min1[i][i]=height[i];
        }
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                max1[i][j]=max(max1[i][j-1],height[j]);
                min1[i][j]=min(min1[i][j-1],height[j]);
            }
        }
        while(q--){
            cin>>seta>>setb;
            cout<<max1[seta-1][setb-1]-min1[seta-1][setb-1]<<endl;
        }
    }
}

但是完成了这样的初步思考之后,由于原问题中的数据范围给的是50000,显然会超过给定的内存范围。

于是,就发现了有线段树这个结构:

#include <iostream>
#include <algorithm>
#define maxn 50000
using namespace std;
int MAX[maxn<<2];
int MIN[maxn<<2];
int A[maxn];
void maximum(int rt){
    MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
}
void minmum(int rt){
    MIN[rt]=min(MIN[rt<<1],MIN[rt<<1|1]);
}
void Build(int l,int r,int rt){
    if(l==r){
        MAX[rt]=A[l];
        MIN[rt]=A[l];
        return ;
    }

    int m=(l+r)>>1;
    Build(l,m,rt<<1);
    Build(m+1,r,rt<<1|1);
    maximum(rt);
    minmum(rt);
}
int Query(int L,int R,int l,int r,int rt,int &x,int &y){
    if(L<=l&&R>=r){
        x=max(MAX[rt],x);
        y=min(MIN[rt],y);
        return x-y;
    }
    int m=(l+r)>>1;
    if(L<=m) {
        return Query(L,R,l,m,rt<<1,x,y);
    }
    else if(R>m){
        return Query(L,R,m+1,r,rt<<1|1,x,y);
    }

}
int main(){
    int n,m,L,R,x,y;
    while(cin>>n>>m){
        for(int i=0;i<n;i++){
            cin>>A[i];
        }
        Build(1,n,1);
        while(m--){
            x=-1,y=300000;
            cin>>L>>R;
        cout<<Query(L,R,1,n,1,x,y)<<endl;
        }
    }

    return 0;
}

就对于上述代码而言是根据查找某段区间的和来修改的,但是还需要对查找部分函数尽心更深一步的了解。

原文地址:https://www.cnblogs.com/Sun-Yiwen-blog/p/9863858.html

时间: 2024-10-12 12:45:18

关于线段树的初步理解的相关文章

函数式线段树的个人理解

这几天一直在搞这个东西,今天总算搞懂了,函数式线段树是一种解决离线算法的数据结构,我是这样理解的,它将所有数据离散化,再对每一个节点 N 建一颗(1,N)的线段树,这是它的思路,当然如果真正的去建这么多线段树,内存肯定爆了,所以这个就是函数式线段树的高级的地方,它从分利用前缀和的思想,后一颗树和前一棵树分享了一半的节点,什么意思呢,若现在我们得到了T[N],也就是(1,N)这些节点的线段树,我们要建(1,N+1)这颗线段树T[N+1],这样我们只需要在T[N]的基础上加入a[N+1]这个节点,如

HDU 1166 敌兵布阵(线段树的初步应用2)

HDU  1166  敌兵布阵 Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况.由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视. 中央情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工

线段树练习题(不断更新中)

通过参考大神们线段树的文章,准备开始要一个一个把上面的题目做一遍了,有很多都是原来做过的,现在也再次做一遍方便以后查阅 打过 * 的表示对别人的想法有所参考,留待以后再做一次 现在比起一开始接触线段树已经更为容易理解了,想到自己暑期集训的时候还是傻傻的背着线段树的格式在做题,不肯动脑子去思考 代码含义,觉得自己会背格式就足够了,后来也一直只会套模版题稍微一变就傻了,现在想想当时懒得动脑筋确实是一个笑话 现在想想最简单来看就是把线段树看成一个能够维护一些自己所需数据的二叉树,通过所维护的数据来判断

树秀于林风必摧之——线段树

关于线段树,其实我一开始也是很懵的,但看久了也就习惯了. 以下是我对线段树的一点理解,写得不好,也请各位看官见谅. 搜狗定义:线段树(Segment Tree)是一种二叉搜索树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 定义还是很显然的. 那么线段树都能做些什么呢? 在n个数中有m个询问,询问如下: 1.把q这个数改成v  O(logn); 2.求在1~n这个区间的和. 接下来我们讲原理(当然原理也是我自己的理解,可能不是正解,但我想,线段树这个东西大概就是这样的吧

杭电 HDU ACM 1754 I Hate It (线段树)

I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 47272    Accepted Submission(s): 18507 Problem Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师

线段树模板 (poj 3468)

之前一直没手写过线段树,今日手写线段树发现模板理解起来还是很容易的,lazy标记的用法也大概了解了一点,但对于线段树的理解应该还不是很好(等学会线段树的时候就学树链剖分,立个flag) 下面是poj3468代码 #include <cstdio> #include <algorithm> #include <iostream> using namespace std; const int maxn=100000+5; int num[maxn]; struct node

[线段树系列] LCT打延迟标记的正确姿势

这一篇博客将教你什么? 如何用LCT打延迟标记,LCT和线段树延迟标记间的关系,为什么延迟标记要这样打. ——正片开始—— 学习这一篇博客前,确保你会以下知识: Link-Cut-Tree,普通线段树 当然,不会也没有关系,你可以先收藏这篇博客,等你学了以后再来看. 最好通过了这一道题:[模板]线段树Ⅱ 没有通过也没关系,对于本篇的知识只是一个启发作用. 我们平时使用的Link-Cut-Tree一般只需要打一个翻转标记rev[x]. 然后我们用pushR(x)函数来下发翻转标记. 那么我们现在来

线段树入门理解

在复习算法至分治法时,书本上主要介绍了合并排序和快速排序,较为简单.特拓展简单学习一个应用了分治法的算法结构--线段树. acm刷题时遇到许多连续区间的动态查询问题,例如求取某一区间上元素之和.求取某一区间上元素的最大值,此时如果使用一般的方法求解会使得时间超出要求.此时需要使用到线段树,其主要用于高效解决连续区间的动态查询问题. 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN),从而大大减少耗时

一步一步理解线段树——转载自JustDoIT

一步一步理解线段树 目录 一.概述 二.从一个例子理解线段树 创建线段树 线段树区间查询 单节点更新 区间更新 三.线段树实战 -------------------------- 一 概述 线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn). 线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)/2)左儿子的区间