线段树学习笔记
定义
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
线段树至少支持下列操作:
Insert(t,x):将包含在区间 int 的元素 x 插入到树t中;
Delete(t,x):从线段树 t 中删除元素 x;
Search(t,x):返回一个指向树 t 中元素 x 的指针。
基本结构
线段树是建立在线段的基础上,每个结点都代表了一条线段[a,b]。长度为1的线段称为元线段。非元线段都有两个子结点,左结点代表的线段为[a,(a + b) / 2],右结点代表的线段为[((a + b) / 2)+1,b]。
下图就是两棵长度范围为[1,5][1,10]的线段树。
长度范围为[1,L] 的一棵线段树的深度为log (L) + 1。这个显然,而且存储一棵线段树的空间复杂度为O(L)。
线段树支持最基本的操作为插入和删除一条线段。下面以插入为例,详细叙述,删除类似。
将一条线段[a,b] 插入到代表线段[l,r]的结点p中,如果p不是元线段,那么令mid=(l+r)/2。如果b<mid,那么将线段[a,b] 也插入到p的左儿子结点中,如果a>mid,那么将线段[a,b] 也插入到p的右儿子结点中。
插入(删除)操作的时间复杂度为O(logn)。
luogu 线段树模板1 代码
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; int n,m,f1,f2,f3,f4,cnt; long long a[100001]; struct kkk{ int a,b; long long num; int mid; long long mark; }tree[400001]; long long ans[100001]; void make(int x,int y,int l) { tree[l].mark=0; tree[l].a=x; tree[l].b=y; tree[l].mid=(y-x)/2+x; if(x==y) { tree[l].num=a[x]; return ; } else { make(x,tree[l].mid,l<<1); make(tree[l].mid+1,y,(l<<1)+1); tree[l].num=tree[l<<1].num+tree[(l<<1)+1].num; return ; } } void down(int l) { if(tree[l].a==tree[l].b) { tree[l].num+=tree[l].mark; tree[l].mark=0; return ; } if(tree[l].mark!=0) { tree[l<<1].mark+=tree[l].mark; tree[(l<<1)+1].mark+=tree[l].mark; tree[l].num+=tree[l].mark*(tree[l].b-tree[l].a+1); tree[l].mark=0; } } void update(int x,int y,int l,int k) { if((x==tree[l].a)&&(y==tree[l].b)) { tree[l].mark+=k; down(l); return ; } down(l); if(y<=tree[l].mid) { update(x,y,l<<1,k); } else if(x>=tree[l].mid+1) { update(x,y,(l<<1)+1,k); } else { update(x,tree[l].mid,l<<1,k); update(tree[l].mid+1,y,(l<<1)+1,k); } tree[l].num=tree[l<<1].num+tree[(l<<1)+1].num+tree[l<<1].mark*(tree[l<<1].b-tree[l<<1].a+1)+tree[(l<<1)+1].mark*(tree[(l<<1)+1].b-tree[(l<<1)+1].a+1); return ; } long long query(int x,int y,int l) { down(l); if((tree[l].a==x)&&(tree[l].b==y)) return tree[l].num; if(y<=tree[l].mid) { return query(x,y,l<<1); } if(x>=tree[l].mid+1) return query(x,y,(l<<1)+1); return query(x,tree[l].mid,l<<1)+query(tree[l].mid+1,y,(l<<1)+1); } using namespace std; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); make(1,n,1); for(int i=1;i<=m;i++) { scanf("%d",&f1); if(f1==1) { scanf("%d%d%d",&f2,&f3,&f4); update(f2,f3,1,f4); } else { scanf("%d%d",&f2,&f3); cnt++; ans[cnt]=query(f2,f3,1); //printf("%d\n",query(f2,f3,1)); } } for(int i=1;i<=cnt;i++) { printf("%lld\n",ans[i]); } //system("pause"); return 0; }