HDU 1754 线段树 及其 入门知识
2012-09-15 20:38
323 查看
刚开始以为看到这道题是提交,ac的人很多,以为是水题,用最简单的方法操作,超时!
线段树!!
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。
实际应用:(源自百度百科)
最简单的应用就是记录线段有否被覆盖,并随时查询当前被覆盖线段的总长度。那么此时可以在结点结构中加入一个变量int count;代表当前结点代表的子树中被覆盖的线段长度和。这样就要在插入(删除)当中维护这个count值,于是当前的覆盖总值就是根节点的count值了。
另外也可以将count换成bool cover;支持查找一个结点或线段是否被覆盖。[1]
实际上,通过在结点上记录不同的数据,线段树还可以完成很多不同的任务。例如,如果每次插入操作是在一条线段上每个位置均加k,而查询操作是计算一条线段上的总和,那么在结点上需要记录的值为sum。
这里会遇到一个问题:为了使所有sum值都保持正确,每一次插入操作可能要更新O(N)个sum值,从而使时间复杂度退化为O(N)。
解决方案是Lazy思想:对整个结点进行的操作,先在结点上做标记,而并非真正执行,直到根据查询操作的需要分成两部分。
根据Lazy思想,我们可以在不代表原线段的结点上增加一个值toadd,即为对这个结点,留待以后执行的插入操作k值的总和。对整个结点插入时,只更新sum和toadd值而不向下进行,这样时间复杂度可证明为O(logN)。
对一个toadd值为0的结点整个进行查询时,直接返回存储在其中的sum值;而若对toadd不为0的一部分进行查询,则要更新其左右子结点的sum值,然后把toadd值传递下去,再对这个查询本身,左右子结点分别递归下去。时间复杂度也是O(logN)。
线段树!!
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。
实际应用:(源自百度百科)
最简单的应用就是记录线段有否被覆盖,并随时查询当前被覆盖线段的总长度。那么此时可以在结点结构中加入一个变量int count;代表当前结点代表的子树中被覆盖的线段长度和。这样就要在插入(删除)当中维护这个count值,于是当前的覆盖总值就是根节点的count值了。
另外也可以将count换成bool cover;支持查找一个结点或线段是否被覆盖。[1]
实际上,通过在结点上记录不同的数据,线段树还可以完成很多不同的任务。例如,如果每次插入操作是在一条线段上每个位置均加k,而查询操作是计算一条线段上的总和,那么在结点上需要记录的值为sum。
这里会遇到一个问题:为了使所有sum值都保持正确,每一次插入操作可能要更新O(N)个sum值,从而使时间复杂度退化为O(N)。
解决方案是Lazy思想:对整个结点进行的操作,先在结点上做标记,而并非真正执行,直到根据查询操作的需要分成两部分。
根据Lazy思想,我们可以在不代表原线段的结点上增加一个值toadd,即为对这个结点,留待以后执行的插入操作k值的总和。对整个结点插入时,只更新sum和toadd值而不向下进行,这样时间复杂度可证明为O(logN)。
对一个toadd值为0的结点整个进行查询时,直接返回存储在其中的sum值;而若对toadd不为0的一部分进行查询,则要更新其左右子结点的sum值,然后把toadd值传递下去,再对这个查询本身,左右子结点分别递归下去。时间复杂度也是O(logN)。
#include <iostream> using namespace std; struct Node { int l,r,mid,max; }node[600000]; void init(int a,int b,int n) { node .l=a; node .max=-1; node .r=b; node .mid=(a+b)/2; if(a+1==b) return; init(a,(a+b)/2,n*2); init((a+b)/2,b,2*n+1); } void update(int pos,int value,int n) { if(value>node .max) node .max=value; if((node .l+1)==node .r) return; if(pos<node .mid) { update(pos,value,2*n); } if(pos>=node .mid) { update(pos,value,2*n+1); } } int query(int a,int b,int n) { if(node .l==a && node .r==b) return node .max; if(a<node .mid) { if(b<=node .mid) { return query(a,b,2*n); } else { return max(query(a,node .mid,2*n),query(node .mid,b,2*n+1)); } } else { return query(a,b,2*n+1); } } int main() { int n,m,t,a,b,i; char ch; while(cin >>n>>m) { init(1,n+1,1); for(i=1;i<=n;i++) { scanf("%d",&t); update(i,t,1); } for(i=1;i<=m;i++) { getchar(); cin >>ch; cin >>a>>b; if(ch=='Q') cout <<query(a,b+1,1)<<endl;//注意是b+1而不是b。因为线段树是一个左开右闭的的区间。 else update(a,b,1); } } return 0; }
相关文章推荐
- hdu 1754 I Hate It (线段树入门题、求区间段的最大值)
- HDU 1754 I Hate It 线段树入门
- hdu 1754 线段树入门(单点替换,区间最值)
- HDU-1754(线段树入门)
- HDU-1754 I Hate It (线段树 入门题 点修改 区间查询)
- 线段树入门HDU_1754
- 线段树入门HDU_1754
- hdu 1754 线段树入门的题
- 线段树 入门总结 hdu 1754 1161
- 线段树入门——HDU 1754
- hdu 1754 I Hate It(线段树入门)
- hdu 1754 I Hate It(线段树入门-单点更新)
- HDU 1754(线段树入门)
- hdu 1754 线段树入门 单点更新
- hdu 1754 i hate it(线段树入门)
- HDU1754——I Hate It(线段树入门)
- HDU 1754 I Hate It(入门线段树)
- HDU 1754 I Hate It(线段树入门题)
- HDU 1754 I Hate It【线段树入门题,单点替换,区间最值】
- hdu 1754:I Hate It(线段树,入门题,RMQ问题)