在写这篇博客之前首先申明一下,这个线段树的名字是我的好队友 shu_mj 取的。由于实在很好用,对于新手容易上手所以写一篇博客造福老百姓。
然后说明一下这仅仅是一篇关于线段树入门的文章。
单点更新:
单点更新不需要标记整个线段树只需要自底向上的维护值(最小值,最大值,和等)就行了。实现难度较低,新手也能很快学会。
在线段树中每个节点的含义都是一个线段根节点表示的是1-n的线段,假设根节点标号是o,他的左子树标号为o<<1,右子树标号为o<<1|1相应的左子树表示的区间为1-m(m=(l+r)>>1),而右子树表示的区间为m+1-r
这样我们用一颗高度为logn 的树即可表示整条线段了。
对于单点更新的线段树每个标记都能下放到最底层,由于每次下放标记最多沿着一路走所以每次操作的复杂度为logn的。
对于求和的时候我们把区间不断的拆分用线段树上的小区间(节点表示的区间)来拼成需要求和的完整区间,最后将这些节点上的标记累加起来即为答案。复杂度同样为logn的。
代码如下:
1 int num[LEN], sum[4*LEN], n; 2 3 void pushup(int o){ 4 sum[o] = sum[o<<1] + sum[o<<1|1]; 5 } 6 7 void build(int l, int r, int o){ 8 if(l == r){ 9 //设初值 10 return ; 11 } 12 int m = (l + r)/2; 13 build(l, m, o<<1); 14 build(m+1, r, o<<1|1); 15 pushup(o); 16 } 17 18 void update(int l, int r, int o, int pos, int val){ 19 if(l == r) { 20 sum[o] += val; 21 return ; 22 } 23 int m = (l + r)/2; 24 if(pos <= m) update(l, m, o<<1, pos, val); 25 else update(m+1, r, o<<1|1, pos, val); 26 pushup(o); 27 } 28 29 int query(int l, int r, int o, int L, int R){ 30 if(l >= L && r <= R) return sum[o]; 31 int m = (l + r) / 2, ret = 0; 32 if(L <= m) ret += query(l, m, o<<1, L, R); 33 if(R > m) ret += query(m+1, r, o<<1|1, L, R); 34 return ret; 35 }