我们解决问题的最好方法就是拿实例来举例子
我们来看tyvj1038或计蒜客 “管家的忠诚”
老管家是一个聪明能干的人。他为财主工作了整整10年,财主为了让自已账目更加清楚。要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意。但是由于一些人的挑拨,财主还是对管家产生了怀疑。于是他决定用一种特别的方法来判断管家的忠诚,他把每次的账目按1,2,3…编号,然后不定时的问管家问题,问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题。
输入中第一行有两个数m,n表示有m(m< =100000)笔账,n表示有n个问题,n< =100000。 第二行为m个数,分别是账目的钱数 后面n行分别是n个问题,每行有2个数字说明开始结束的账目编号。
输出文件中为每个问题的答案。具体查看样例。
样例输入
10 3
1 2 3 4 5 6 7 8 9 10
2 7
3 9
1 10
样例输出
2 3 1
一道最最最基础的线段树的题,单点更新,求区间最小值,我们先来看两组代码,是两个不同的查询函数(query).
1 struct nod 2 { 3 int l,r; 4 int data; 5 }tree[5*N];
block 1:
1 int query(int i,int l,int r) 2 { 3 4 if(l<=tree[i].l&&tree[i].r<=r) 5 { 6 return tree[i].data; 7 } 8 int mid=(tree[i].l+tree[i].r)/2; 9 int ret = 99999999; 10 if(l<=mid) 11 ret = min(ret,query(i*2,l,r)); 12 if(r>mid) 13 ret = min(ret,query(i*2+1,l,r)); 14 return ret; 15 }
来先看一下block 1 的10和12行,再对比一下block 2 的8和10行,是不是发现l和r反了呢?
到底哪个是对的,哪个是错的?
block 2:
1 int query(int i,int l,int r) 2 { 3 if(l<=tree[i].l&&tree[i].r<=r) 4 { 5 return tree[i].data; 6 } 7 int mid=(tree[i].l+tree[i].r)/2; 8 if(r<=mid) 9 return query(i*2,l,r); 10 if(l>mid) 11 return query(i*2+1,l,r); 12 return min(query(i*2,l,r),query(i*2+1,l,r)); 13 }
我一开始学线段树的时候,也就是昨天...想找一个好一点的模板,但发现这两个模板好像不太一样,r和l完全是反的,尼玛!这肯定有一个是错的啊,这尼玛写错了不是误人子弟!!!幸好老夫多看了几个模板,要不然就被坑死了!!
随着时间的推进,窝终于对线段树的理解越来越深,终于茅塞顿开!原来两个都是对的~~
我现在来解析一下:
block 2是我采用的类型,第8行的意思是如果你查询的右端都小于当前区间的mid了,那当前区间的data(视题目而定),肯定全部由左孩子过来的,直接返回左孩子的值,和右孩子没卵关系!第10行同理
block 1是老师采用的类型,第10行的意思是只要你查询区间的左端小于当前区间的mid,那肯定有一部分是从左孩子来的,我先把你和ret比较一下,最小值记录到ret里,12行的意思,只要你查询区间的右端大于当前区间的mid,那肯定有一部分是从右孩子来的,我再把你和当前ret比较一下,最小值记录到ret里。最后我再输出ret。
觉得怎么样呢,理解了没有?没有理解做一下这道题吧,理解会更深~
附上我的题解http://www.cnblogs.com/liwenchi/p/5760660.html