线段树详解(洛谷模板题)
来源:互联网 发布:艾力绅 知乎 家用 编辑:程序博客网 时间:2024/06/05 11:47
一、算法概况
线段树是一种二叉搜索树,它将一个区间分成多个单元区间,每个单元区间对应线段树的一个叶子节点,有查询区间和,查询区间最大值、最小值等功能(本篇讲解的是求区间和的代码),由于它二叉结构的特性,使得他的操作复杂读为O(logN)
线段树的根节点代表所要维护的值在总区间 [a,b] 的值,他的左子节点代表区间 [a,(a+b)/2] ,他的右子节点代表区间 [(a+b)/2+1,b],对他的左右子节点也同样如此划分。
线段树的操作大致分为建树、区间修改,下放延迟标记,区间查询等操作。
二、分步详解
1.建树
void build(int root,int istart,int iend){//建树 if(istart==iend){//如果区间的左端点等于右端点,即为到达了最下端的叶子节点mem[root].sum=a[istart];//叶子节点的值就是对应的点的值return;}int mid=(istart+iend)/2;build(root*2,istart,mid);//建左子树build(root*2+1,mid+1,iend);//建右子数mem[root].sum=mem[2*root].sum+mem[2*root+1].sum;//区间预处理求和,父节点的值是两个子节点值的和}
2.区间修改
void update(int root,int istart,int iend,int l,int r,int value){// 修改区间 if(r<istart||l>iend) //如果查询的区间在我们目前找到的区间之外,就return,不寻找return;if(l<=istart&&r>=iend){ //如果我们找到了对应区间mem[root].addmark+=value; //我们将root节点的延迟标记加上value(value是我们在这个区间加上的值) mem[root].sum+=value*(iend-istart+1); //由于root节点对应的区间的每个节点都会加上value,所以root节点的sum值会加上value值×对应区间节点的个数return;}pushdown(root,iend-istart+1); //下方延迟标记(线段树的精髓)int mid=(istart+iend)/2;update(root*2,istart,mid,l,r,value); //修改左子节点update(root*2+1,mid+1,iend,l,r,value); //修改右子节点mem[root].sum=mem[root*2].sum+mem[root*2+1].sum; //区间再次求和,父节点的值是两个子节点的值的和}
3.下放延迟标记
如果我们在修改区间时,一次全部修改,线段树的复杂度将大大增加,但如果我们加入了延迟标记,在我们需要使用该区间时进行下放延迟标记,可以将线段树大大优化
void pushdown(int root,int len){//下放标记 if(mem[root].addmark!=0){//如果root节点有延迟标记mem[2*root].sum+=mem[root].addmark*(len-len/2);//对左子节点下放标记时,同时求和mem[2*root].addmark+=mem[root].addmark;//左子节点所代表的区间也在父节点代表的区间内,所以他们的延迟标记应该相同mem[2*root+1].sum+=mem[root].addmark*(len/2);//对右子节点下放标记时,同时求和mem[2*root+1].addmark+=mem[root].addmark;//同左子节点mem[root].addmark=0;//由于父节点的标记已经下放给子节点,我们消除父节点的标记}}
4.查询区间和
long long query(int root,int istart,int iend,int l,int r){//查询区间 ,注意用long longif(iend<l||istart>r)//如果我们当前的区间不在我们所要查询的区间内,return 0return 0;if(istart>=l&&iend<=r){//我们找到了对应区间return mem[root].sum;//返回对应的区间和}pushdown(root,iend-istart+1);//下放我们当前正确查找区间的延迟标记int mid=(istart+iend)/2;return query(root*2,istart,mid,l,r)+query(root*2+1,mid+1,iend,l,r);//父节点的值等于左右子节点的值和,我们在此递归求解}
三、总代码
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#define maxn 1e6+10using namespace std;int n,m,k;int a[1000010];struct node{long long addmark=0,sum;//addmark延迟标记,sum区间和 }mem[1000010];void build(int root,int istart,int iend){//建树 if(istart==iend){mem[root].sum=a[istart];return;}int mid=(istart+iend)/2;build(root*2,istart,mid);build(root*2+1,mid+1,iend);mem[root].sum=mem[2*root].sum+mem[2*root+1].sum;}void pushdown(int root,int len){//下放标记 if(mem[root].addmark!=0){mem[2*root].sum+=mem[root].addmark*(len-len/2);//下放标记时同时求和 mem[2*root].addmark+=mem[root].addmark;mem[2*root+1].sum+=mem[root].addmark*(len/2);mem[2*root+1].addmark+=mem[root].addmark;mem[root].addmark=0;//消除父节点的标记 }}void update(int root,int istart,int iend,int l,int r,int value){// 修改区间 if(r<istart||l>iend)return;if(l<=istart&&r>=iend){mem[root].addmark+=value;mem[root].sum+=value*(iend-istart+1);return;}pushdown(root,iend-istart+1);int mid=(istart+iend)/2;update(root*2,istart,mid,l,r,value);update(root*2+1,mid+1,iend,l,r,value);mem[root].sum=mem[root*2].sum+mem[root*2+1].sum;}long long query(int root,int istart,int iend,int l,int r){//查询区间 if(iend<l||istart>r)return 0;if(istart>=l&&iend<=r){return mem[root].sum;}pushdown(root,iend-istart+1);int mid=(istart+iend)/2;return query(root*2,istart,mid,l,r)+query(root*2+1,mid+1,iend,l,r);}int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&a[i]);}build(1,1,n);for(int i=1;i<=m;i++){int q,l,r;scanf("%d%d%d",&q,&l,&r);if(q==1){scanf("%d",&k);update(1,1,n,l,r,k);}else{printf("%lld\n",query(1,1,n,l,r));}}return 0;}
四、总结
线段树一般在noip中不作为正解出现,不过是暴力的不二之选,只要搞懂了线段树四种操作,线段树就可以基本理解了。
阅读全文
0 0
- 线段树详解(洛谷模板题)
- 线段树模板+详解
- 线段树模板详解
- 线段树 各种模板(详解)
- 线段树模板题
- 线段树(模板)
- 线段树(模板)
- (线段树)模板
- 【模板】线段树 区间加,区间求和 (模板题:P3372线段树1)
- hdu_1754_线段树模板题
- hdu1166(线段树模板题)
- 二维线段树 模板题
- HDU1754 线段树模板题
- 线段树模板(poj3468)
- 线段树模板(算法)
- 线段树(学习模板)
- 线段树(简单模板)
- LCA详解(洛谷模板题)
- 动态规划之三
- 常用SQL语句
- PHP常用函数
- 在MFC中调用深度学习caffe,出现的问题。
- Activity的生命周期
- 线段树详解(洛谷模板题)
- C#调用cmd命令执行msi安装文件
- 三种方式获取Class
- Java运行时数据区域
- iOS 各种demo链接汇总~模糊效果
- 使用eclipse自带的git从github上clone和上传项目
- 博客搬家
- IntelliJ IDEA中怎么查看方法说明快捷键
- 设置点击事件的工具类