线段树2 求区间最小值

线段树2 求区间最小值

从数组arr[0...n-1]中查找某个数组某个区间内的最小值,其中数组大小固定,但是数组中的元素的值可以随时更新。

数组[2, 5, 1, 4, 9, 3]可以构造如下的二叉树(背景为白色表示叶子节点,非叶子节点的值是其对应数组区间内的最小值,例如根节点表示数组区间arr[0...5]内的最小值是1):

线段树的四种操作:

1、线段树的创建

2、线段树的查询

3、线段树的更新单节点

4、线段树的更新区间

直接上完整代码吧

  1 #include <bits/stdc++.h>
  2 #define INFINITE 0x3fffffff
  3 using namespace std;
  4 void printTree();
  5 //数组[2, 5, 1, 4, 9, 3]
  6
  7 const int MAXNUM = 1000;
  8 int arr[6]={2,5,1,4,9,3};
  9 int n=6;
 10 struct SegTreeNode
 11 {
 12     int val;
 13     int addMark;
 14 }segTree[MAXNUM];//定义线段树
 15
 16 /*
 17 功能:当前节点的标志域向孩子节点传递
 18 root: 当前线段树的根节点下标
 19 */
 20 void pushDown(int root)
 21 {
 22     if(segTree[root].addMark != 0)
 23     {
 24         //设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递
 25         //所以是 “+=”
 26         segTree[root*2+1].addMark += segTree[root].addMark;
 27         segTree[root*2+2].addMark += segTree[root].addMark;
 28         //根据标志域设置孩子节点的值。因为我们是求区间最小值,因此当区间内每个元
 29         //素加上一个值时,区间的最小值也加上这个值
 30         segTree[root*2+1].val += segTree[root].addMark;
 31         segTree[root*2+2].val += segTree[root].addMark;
 32         //传递后,当前节点标记域清空
 33         segTree[root].addMark = 0;
 34     }
 35 }
 36
 37
 38 /*
 39 功能:构建线段树
 40 root:当前线段树的根节点下标
 41 arr: 用来构造线段树的数组
 42 istart:数组的起始位置
 43 iend:数组的结束位置
 44 */
 45 void build(int root, int arr[], int istart, int iend)
 46 {
 47     segTree[root].addMark = 0;//----设置标延迟记域
 48     if(istart == iend)//叶子节点
 49         segTree[root].val = arr[istart];
 50     else
 51     {
 52         int mid = (istart + iend) / 2;
 53         //从0节点开始计数,所以左孩子是root*2+1
 54         build(root*2+1, arr, istart, mid);//递归构造左子树
 55         build(root*2+2, arr, mid+1, iend);//递归构造右子树
 56         //根据左右子树根节点的值,更新当前根节点的值
 57         segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
 58     }
 59 }
 60
 61
 62 /*
 63 功能:线段树的区间查询
 64 root:当前线段树的根节点下标
 65 [nstart, nend]: 当前节点所表示的区间
 66 [qstart, qend]: 此次查询的区间
 67 */
 68 int query(int root, int nstart, int nend, int qstart, int qend)
 69 {
 70     //查询区间和当前节点区间没有交集
 71     if(qstart > nend || qend < nstart)
 72         return INFINITE;
 73     //当前节点区间包含在查询区间内
 74     if(qstart <= nstart && qend >= nend)
 75         return segTree[root].val;
 76     //分别从左右子树查询,返回两者查询结果的较小值
 77     pushDown(root); //延迟标记向下传递
 78     int mid = (nstart + nend) / 2;
 79     return min(query(root*2+1, nstart, mid, qstart, qend),
 80                query(root*2+2, mid + 1, nend, qstart, qend));
 81
 82 }
 83
 84
 85 /*
 86 功能:更新线段树中某个叶子节点的值
 87 root:当前线段树的根节点下标
 88 [nstart, nend]: 当前节点所表示的区间
 89 index: 待更新节点在原始数组arr中的下标
 90 addVal: 更新的值(原来的值加上addVal)
 91 */
 92 void updateOne(int root, int nstart, int nend, int index, int addVal)
 93 {
 94     if(nstart == nend)
 95     {
 96         if(index == nstart)//找到了相应的节点,更新之
 97             segTree[root].val += addVal;
 98         return;
 99     }
100     int mid = (nstart + nend) / 2;
101     if(index <= mid)//在左子树中更新
102         updateOne(root*2+1, nstart, mid, index, addVal);
103     else updateOne(root*2+2, mid+1, nend, index, addVal);//在右子树中更新
104     //根据左右子树的值回溯更新当前节点的值
105     segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
106 }
107
108
109
110 /*
111 功能:更新线段树中某个区间内叶子节点的值
112 root:当前线段树的根节点下标
113 [nstart, nend]: 当前节点所表示的区间
114 [ustart, uend]: 待更新的区间
115 addVal: 更新的值(原来的值加上addVal)
116 */
117 void update(int root, int nstart, int nend, int ustart, int uend, int addVal)
118 {
119     //更新区间和当前节点区间没有交集
120     if(ustart > nend || uend < nstart)
121         return ;
122     //当前节点区间包含在更新区间内
123     if(ustart <= nstart && uend >= nend)
124     {
125         segTree[root].addMark += addVal;
126         segTree[root].val += addVal;
127         return ;
128     }
129     pushDown(root); //延迟标记向下传递
130     //更新左右孩子节点
131     int mid = (nstart + nend) / 2;
132     update(root*2+1, nstart, mid, ustart, uend, addVal);
133     update(root*2+2, mid+1, nend, ustart, uend, addVal);
134     //根据左右子树的值回溯更新当前节点的值
135     segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
136 }
137
138
139 int main(){
140     //建树
141     build(0,arr,0,5);
142     //printTree();
143     //cout<<query(0,0,5,0,3)<<endl;
144     //updateOne(0,0,5,3,6);//将第3+1哥元素的值增加6
145     //printTree();
146     update(0,0,5,0,2,2);//0-2区间的值都增加2
147     //segTree[1].addMark=2;
148     printTree();
149     cout<<query(0,0,5,0,1)<<endl;
150     printTree();
151
152     return 0;
153 }
154
155 void printTree(){
156     for(int i=0;i<15;i++){
157         cout<<segTree[i].val<<" ";
158     }
159     cout<<endl;
160 }
时间: 2024-10-20 07:12:52

线段树2 求区间最小值的相关文章

线段树模板(区间最小值优化 版) (RMQ with Shifts)

题意: #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <fstream> using namespace std; const int maxn =100010; int RMQ[maxn<<2]; int str[maxn]; int N,M; char ctr[35]; int total[35],cn

hdu2852--KiKi&#39;s K-Number(线段树,求第k个数)

KiKi's K-Number Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2546    Accepted Submission(s): 1174 Problem Description For the k-th number, we all should be very familiar with it. Of course,t

UPC 2224 Boring Counting (离线线段树,统计区间[l,r]之间大小在[A,B]中的数的个数)

题目链接:http://acm.upc.edu.cn/problem.php?id=2224 题意:给出n个数pi,和m个查询,每个查询给出l,r,a,b,让你求在区间l~r之间的pi的个数(A<=pi<=B,l<=i<=r). 参考链接:http://www.cnblogs.com/zj62/p/3558967.html #include <iostream> #include <cstdio> #include <cstring> #incl

【HDU】1754 I hate it ——线段树 单点更新 区间最值

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

线段树 : 求矩形面积的并 ---- hnu : 12884 Area Coverage

Area Coverage Time Limit: 10000ms, Special Time Limit:2500ms, Memory Limit:65536KB Total submit users: 16, Accepted users: 12 Problem 12884 : No special judgement Problem description In this day and age, a lot of the spying on other countries is done

HDOJ--4893--Wow! Such Sequence!【线段树+单点、区间更新】

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4893 题意:给你一个长度n的数列,初始都为0,有三种操作,第一种给第k个位置的数加d,第二种是查询区间 [l , r] 的总和,第三种是使区间 [l , r] 的值改为离它最近的那个斐波那契数的值. 我刚开始用sum数组存储节点的值,第三种操作是个区间更新,但是区间更新的值不一样,我就想当然的搜到最底部的节点来处理更新,果断T了.后来想了想,其实可以在节点上再加一个信息,就是这个值下次进行第三种操作要变

hdu - 4973 - A simple simulation problem.(线段树单点更新 + 区间更新)

题意:初始序列 1, 2, ..., n,m次操作(1 <= n,m<= 50000),每次操作可为: D l r,将区间[l, r]中的所有数复制一次: Q l r,输出区间[l, r]中同一数字个数的最大值. (0 <= r – l <= 10^8, 1 <= l, r <= 序列元素个数) 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4973 -->>因为区间内数字是依次递增的,所以可以以数字为叶建线段

hdu 3308 线段树单点更新 区间合并

http://acm.hdu.edu.cn/showproblem.php?pid=3308 学到两点: 1.以区间端点为开始/结束的最长......似乎在Dp也常用这种思想 2.分类的时候,明确标准逐层分类,思维格式: 条件一成立: { 条件二成立: { } else { } } else { 条件二成立: { } else { } } 上面的这种方式很清晰,如果直接想到那种情况iif(条件一 &条件二)就写,很容易出错而且把自己搞乱,或者情况不全,,,我就因为这WA了几次 3.WA了之后 ,

hdu2795(线段树单点更新&amp;区间最值)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2795 题意:有一个 h * w 的板子,要在上面贴 n 条 1 * x 的广告,在贴第 i 条广告时要尽量将其靠上贴,并输出其最上能贴在哪个位置: 思路:可以将每行剩余空间大小存储到一个数组中,那么对于当前 1 * x 的广告,只需找到所有剩余空间大于的 x 的行中位置最小的即可: 不过本题数据量为 2e5,直接暴力因该会 tle.可以用个线段树维护一下区间最大值,然后查询时对线段树二分即可: 代码