线段树
线段树
呃呃呃,两个月没写博客了。今天突然心血来潮,就把原来一篇只放了一个链接的线段树的博客删了,再写个新的。
线段树,是一种二叉搜索树......这种介绍就不多说了,都是在百度上看的。放个链接自己去看吧。
链接
反正线段树就是一种比较牛逼的数据结构,可以快速解决区间里的某些问题。
例如什么单点修改,单点查询,区间修改,区间查询......
其实线段树的代码不是很难,主要就是把几个部分组合起来。
在介绍这几个部分之前,先说一下两个贼好用的define,分别代表这个点的左儿子和右儿子。
左儿子:#define lson l,m,rt<<1
右儿子:#define rson m+1,r,rt<<1|1
①上传
把左右儿子节点的信息传给父亲节点。
1 void pushup(int rt){ 2 tree[rt]=tree[rt<<1]+tree[rt<<1|1]; 3 }
②建树
建造该线段树。具体过程为不断往下找左右儿子,直到一个儿子只包括一个节点,直接把值输入到该节点并改变所有与这个点相关的父亲节点。
1 void build(int l,int r,int rt){ 2 if(l==r){ 3 scanf("%d",&tree[rt]); 4 return; 5 } 6 int m=(l+r)>>1; 7 build(lson); 8 build(rson); 9 pushup(rt); 10 }
③更新
改变一个点的值。具体过程为不断往下找左右儿子,直到一个儿子只包括一个节点,直接改变这个节点的值并改变所有与这个点相关的父亲节点。
1 void update(int l,int r,int rt,int L,int R){ 2 if(l==r){ 3 tree[rt]+=R; 4 return; 5 } 6 int m=(l+r)>>1; 7 if(L<=m) 8 update(lson,L,R); 9 else 10 update(rson,L,R); 11 pushup(rt); 12 }
④查询
查询一个区间的值。具体过程为不断往下找左右儿子,直到一个儿子都包含在需要查询的区间内,直接返回这个儿子的值。
1 int query(int l,int r,int rt,int L,int R){ 2 if(L<=l&&r<=R) 3 return tree[rt]; 4 int m=(l+r)>>1,ans=0; 5 if(L<=m) 6 ans+=query(lson,L,R); 7 if(m<R) 8 ans+=query(rson,L,R); 9 return ans; 10 }
最简单的线段树的基本代码就是这些,下面献上完整的代码。
1 #include<cstdio> 2 #define lson l,m,rt<<1 3 #define rson m+1,r,rt<<1|1 4 int tree[42521],n,m,a,b,c; 5 void pushup(int rt){ 6 tree[rt]=tree[rt<<1]+tree[rt<<1|1]; 7 } 8 void build(int l,int r,int rt){ 9 if(l==r){ 10 scanf("%d",&tree[rt]); 11 return; 12 } 13 int m=(l+r)>>1; 14 build(lson); 15 build(rson); 16 pushup(rt); 17 } 18 void update(int l,int r,int rt,int L,int R){ 19 if(l==r){ 20 tree[rt]+=R; 21 return; 22 } 23 int m=(l+r)>>1; 24 if(L<=m) 25 update(lson,L,R); 26 else 27 update(rson,L,R); 28 pushup(rt); 29 } 30 int query(int l,int r,int rt,int L,int R){ 31 if(L<=l&&r<=R) 32 return tree[rt]; 33 int m=(l+r)>>1,ans=0; 34 if(L<=m) 35 ans+=query(lson,L,R); 36 if(m<R) 37 ans+=query(rson,L,R); 38 return ans; 39 } 40 int main(){ 41 scanf("%d%d",&n,&m); 42 build(1,n,1); 43 for(int i=1;i<=m;++i){ 44 scanf("%d%d%d",&a,&b,&c); 45 if(a==1) 46 update(1,n,1,b,c); 47 else 48 printf("%d\n",query(1,n,1,b,c)); 49 } 50 return 0; 51 }
不要问我数组大小为什么是42521,我是不会告诉你们的哈哈哈。
不好意思我另一个人格又犯病了。
没事没事咱们继续哈。
求区间最小值,最大值等等的代码其实也差不多,只需要把查询和上传阶段稍加修改。
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 #define lson l,m,rt<<1 5 #define rson m+1,r,rt<<1|1 6 int tree[42521],n,m,a,b,c; 7 void pushup(int rt){ 8 tree[rt]=min(tree[rt<<1],tree[rt<<1|1]); 9 } 10 void build(int l,int r,int rt){ 11 if(l==r){ 12 scanf("%d",&tree[rt]); 13 return; 14 } 15 int m=(l+r)>>1; 16 build(lson); 17 build(rson); 18 pushup(rt); 19 } 20 void update(int l,int r,int rt,int L,int R){ 21 if(l==r){ 22 tree[rt]=R; 23 return; 24 } 25 int m=(l+r)>>1; 26 if(L<=m) 27 update(lson,L,R); 28 else 29 update(rson,L,R); 30 pushup(rt); 31 } 32 int query(int l,int r,int rt,int L,int R){ 33 if(L<=l&&r<=R) 34 return tree[rt]; 35 int m=(l+r)>>1,ans=424242521; 36 if(L<=m) 37 ans=min(ans,query(lson,L,R)); 38 if(m<R) 39 ans=min(ans,query(rson,L,R)); 40 return ans; 41 } 42 int main(){ 43 scanf("%d%d",&n,&m); 44 build(1,n,1); 45 for(int i=1;i<=m;++i){ 46 scanf("%d%d%d",&a,&b,&c); 47 if(a==1) 48 update(1,n,1,b,c); 49 else 50 printf("%d\n",query(1,n,1,b,c)); 51 } 52 return 0; 53 }
像我这种懒癌晚期是不会给你们再搞别的变形了,自己改去吧。
上面只介绍了单点修改,那就再来说一下区间修改。
区间修改比单点修改只多了一点,那就是懒标记。懒标记记录了这个区间都改变了什么,在每次修改或查询某区间时,都要考虑这个懒标记。
⑤标记
标记这里其实并不复杂,就是把这个点的懒标记传给它的左右儿子,并更新它左右儿子的数值,最后清空这个点的懒标记。
1 void pushdown(int rt,int RT){ 2 if(lazy[rt]==0) 3 return; 4 lazy[rt<<1]+=lazy[rt]; 5 tree[rt<<1]+=lazy[rt]*(RT-(RT>>1)); 6 lazy[rt<<1|1]+=lazy[rt]; 7 tree[rt<<1]+=lazy[rt]*(RT>>1); 8 lazy[rt]=0; 9 }
除了懒标记,就没有别的什么了。
那么就放上完整的代码吧。
1 #include<cstdio> 2 #define lson l,m,rt<<1 3 #define rson m+1,r,rt<<1|1 4 int tree[42521],lazy[42521],n,m,a,b,c,d; 5 void pushup(int rt){ 6 tree[rt]=tree[rt<<1]+tree[rt<<1|1]; 7 } 8 void pushdown(int rt,int RT){ 9 if(lazy[rt]==0) 10 return; 11 lazy[rt<<1]+=lazy[rt]; 12 tree[rt<<1]+=lazy[rt]*(RT-(RT>>1)); 13 lazy[rt<<1|1]+=lazy[rt]; 14 tree[rt<<1]+=lazy[rt]*(RT>>1); 15 lazy[rt]=0; 16 } 17 void build(int l,int r,int rt){ 18 if(l==r){ 19 scanf("%d",&tree[rt]); 20 return; 21 } 22 int m=(l+r)>>1; 23 build(lson); 24 build(rson); 25 pushup(rt); 26 } 27 void update(int l,int r,int rt,int L,int R,int RT){ 28 if(L<=l&&r<=R){ 29 lazy[rt]+=RT; 30 tree[rt]+=RT*(r-l+1); 31 return; 32 } 33 pushdown(rt,r-l+1); 34 int m=(l+r)>>1; 35 if(L<=m) 36 update(lson,L,R,RT); 37 if(m<R) 38 update(rson,L,R,RT); 39 pushup(rt); 40 } 41 int query(int l,int r,int rt,int L,int R){ 42 if(L<=l&&r<=R) 43 return tree[rt]; 44 pushdown(rt,r-l+1); 45 int m=(l+r)>>1,ans=0; 46 if(L<=m) 47 ans+=query(lson,L,R); 48 if(m<R) 49 ans+=query(rson,L,R); 50 return ans; 51 } 52 int main(){ 53 scanf("%d%d",&n,&m); 54 build(1,n,1); 55 for(int i=1;i<=m;++i){ 56 scanf("%d",&a); 57 if(a==1){ 58 scanf("%d%d%d",&b,&c,&d); 59 update(1,n,1,b,c,d); 60 } 61 else{ 62 scanf("%d%d",&b,&c); 63 printf("%d\n",query(1,n,1,b,c)); 64 } 65 } 66 return 0; 67 }