线段树基础
Posted wolf940509
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树基础相关的知识,希望对你有一定的参考价值。
关于线段树的原理学习,可以参看杨弋大牛的论文《线段树》以及刘汝佳老师的《算法竞赛入门经典(训练指南)》,代码风格学习hzwer或者notonlysuccess均可。
一.单点更新
最基础的线段树
题目:codevs1080
链接:http://codevs.cn/problem/1080/
分析:最简单的线段树,单点更新,区间求和
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn=100000+10; 5 struct data{ 6 int left,right; //左右端点 7 int sum; //结点的和 8 }Tree[maxn<<2]; 9 int n,m; 10 int a[maxn]; 11 //建树过程 12 void build(int l,int r,int rt){ 13 Tree[rt].left=l;Tree[rt].right=r; 14 if(l==r){ //叶结点直接插入值 15 Tree[rt].sum=a[l]; 16 return; 17 } 18 int mid=(l+r)>>1; 19 build(l,mid,rt<<1); 20 build(mid+1,r,rt<<1|1); 21 Tree[rt].sum=Tree[rt<<1].sum+Tree[rt<<1|1].sum; 22 } 23 24 //单点更新 25 void update(int x,int y,int rt){ 26 Tree[rt].sum+=y; 27 int l=Tree[rt].left,r=Tree[rt].right; 28 if(l==r) return; 29 int mid=(l+r)>>1; 30 if(x<=mid) update(x,y,rt<<1); 31 else update(x,y,rt<<1|1); 32 } 33 34 //区间求和 35 int query(int l,int r,int rt){ 36 int L=Tree[rt].left,R=Tree[rt].right; 37 if(L==l&&r==R){ 38 return Tree[rt].sum; 39 } 40 int mid=(L+R)/2; 41 if(r<=mid) return query(l,r,rt<<1); 42 if(l>mid) return query(l,r,rt<<1|1); 43 return query(l,mid,rt<<1)+query(mid+1,r,rt<<1|1); 44 } 45 int main() 46 { 47 scanf("%d",&n); 48 for(int i=1;i<=n;i++){ 49 scanf("%d",&a[i]); 50 } 51 build(1,n,1); 52 scanf("%d",&m); 53 while(m--){ 54 int num,x,y; 55 scanf("%d%d%d",&num,&x,&y); 56 if(num==1) update(x,y,1); 57 else printf("%d\\n",query(x,y,1)); 58 } 59 return 0; 60 }
二.区间更新
从单点更新一直到区间更新是初学者的一道坎,主要我们要深刻理解lazy操作,因为区间更新有时候并不需要像单点那样更新到子节点
推荐一篇博客:http://blog.csdn.net/dreamzuora/article/details/52781831
题目:codevs1082
链接:http://codevs.cn/problem/1082/
分析:区间更新,区间求和
1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 using namespace std; 5 const int maxn=200000+10; 6 struct data{ 7 int left,right; 8 long long sum; //区间和 9 int val; //标记延迟 10 }Tree[maxn<<2]; 11 int a[maxn]; 12 int n,m; 13 14 //建树 15 void build(int l,int r,int rt){ 16 Tree[rt].left=l;Tree[rt].right=r; 17 if(l==r){ 18 Tree[rt].sum=a[l]; 19 return; 20 } 21 int mid=(l+r)>>1; 22 Tree[rt].val=0; 23 build(l,mid,rt<<1); 24 build(mid+1,r,rt<<1|1); 25 Tree[rt].sum=Tree[rt<<1].sum+Tree[rt<<1|1].sum; 26 } 27 28 //更新子节点 29 void pushdown(int rt){ 30 int x=Tree[rt].right-Tree[rt].left+1; 31 Tree[rt<<1].val+=Tree[rt].val; 32 Tree[rt<<1|1].val+=Tree[rt].val; 33 Tree[rt<<1].sum+=(x-(x>>1))*Tree[rt].val; 34 Tree[rt<<1|1].sum+=(x>>1)*Tree[rt].val; 35 Tree[rt].val=0; 36 } 37 38 //区间更新 39 void update(int l,int r,int x,int rt){ 40 int L=Tree[rt].left,R=Tree[rt].right; 41 if(L==l&&R==r){ 42 Tree[rt].val+=x; 43 Tree[rt].sum+=(R-L+1)*x; 44 return; 45 } 46 int mid=(L+R)>>1; 47 if(Tree[rt].val) pushdown(rt); 48 if(r<=mid) update(l,r,x,rt<<1); 49 else if(l>mid) update(l,r,x,rt<<1|1); 50 else{ 51 update(l,mid,x,rt<<1); 52 update(mid+1,r,x,rt<<1|1); 53 } 54 Tree[rt].sum=Tree[rt<<1].sum+Tree[rt<<1|1].sum; 55 } 56 57 //区间查询 58 long long query(int l,int r,int rt){ 59 int L=Tree[rt].left,R=Tree[rt].right; 60 if(L==l&&R==r){ 61 return Tree[rt].sum; 62 } 63 if(Tree[rt].val) pushdown(rt); 64 int mid=(L+R)>>1; 65 if(r<=mid) return query(l,r,rt<<1); 66 if(l>mid) return query(l,r,rt<<1|1); 67 return query(l,mid,rt<<1)+query(mid+1,r,rt<<1|1); 68 } 69 int main() 70 { 71 scanf("%d",&n); 72 for(int i=1;i<=n;i++) 73 scanf("%d",&a[i]); 74 build(1,n,1); 75 scanf("%d",&m); 76 while(m--){ 77 int num; 78 int x,y,d; 79 scanf("%d",&num); 80 if(num==1){ 81 scanf("%d%d%d",&x,&y,&d); 82 update(x,y,d,1); 83 }else{ 84 scanf("%d%d",&x,&y); 85 printf("%lld\\n",query(x,y,1)); 86 } 87 } 88 }
以上是关于线段树基础的主要内容,如果未能解决你的问题,请参考以下文章