[C++]线段树 区间修改 区间查询
线段树 区间修改 区间查询
算法思想
这次要引入一个核心变量:
lazy 懒标记
为了达到区间修改的目的
又为了减少运算量
所以就需要引入懒标记这个变量
用来满足 即用即推
没有用到的时候便以懒标记的形式存在线段中
子线段要用了便向下推行 \(lazy\)
举个例子:

如果我们只用红色这个线段
那就不用向下推
把 \(lazy\) 留在这里

但是如果要用到橙色的部分
那就需要把 \(lazy\) 下推到橙色部分
代码实现
变量含义详见上个Bolg
add函数
void add(int i,int l,int r,int k){
if(l <= tree[i].l && tree[i].r <= r){
tree[i].sum += k * (tree[i].r - tree[i].l + 1);
tree[i].lazy += k;
return ;
}
if(tree[i].lazy != 0) push(i);
if(tree[li].r >= l)
add(li,l,r,k);
if(tree[ri].l <= r)
add(ri,l,r,k);
tree[i].sum = tree[li].sum + tree[ri].sum;
return ;
}
我们会发现这个函数会调用到 push函数
因此我先解释一下 push函数 的作用:
将当前的 lazy标记 推到下一层
然后我们再来看代码
- \(l <= tree[i].l\) && \(tree[i].r <= r\)
这表示当前线段已经完全在范围包裹里了
对 \(sum\) 的操作:
把 \(k\) 的值分配到当前线段
有 \(tree[i].r - tree[i].l + 1\) 个数
那 \(k\) 就需要乘上 \(tree[i].r - tree[i].l + 1\)
对 \(lazy\) 的操作:
直接加上 \(k\)
要注意这里的 \(lazy\) 是上一轮 add留下来的懒标记
(如果 \(lazy\) 不为 0 的话)
因为不需要向下推
所以在原来的基础上直接加就行了
- \(tree[i].lazy != 0\)
如果运行到这里
说明区间和线段有交叉的部分
那我们就需要二分然后往下了
这时候 \(lazy\) 就不能再留在上一层了
需要用 \(push函数\) 把 \(lazy\) 推下去
怎么推会在后面讲 这里可以先这样理解
- \(tree[li].r >= l\)
- \(tree[ri].l <= r\)
这里二分向下推的操作和之前相同
便不再赘叙
当然 最后还需要吧 \(sum\) 的值更新
push函数
void push(int i){
tree[li].lazy += tree[i].lazy;
tree[ri].lazy += tree[i].lazy;
int mid_ = (tree[i].l + tree[i].r) >> 1;
tree[li].sum += tree[i].lazy * (mid_ - tree[li].l + 1);
tree[ri].sum += tree[i].lazy * (tree[i].r - mid_);
tree[i].lazy = 0;
return ;
}
前面有提到这个函数时用来下推 \(lazy\) 的
那这里就来看看它是如何做到的
首先先把两个子线段的 \(lazy\) 复制为当前线段的懒标记
再把 \(sum\) 给加上
然后让线段的 \(lazy\) 归为 0
search函数
int search(int i,int l,int r){
if(l <= tree[i].l && tree[i].r <= r)
return tree[i].sum;
push(i);
int ans = 0;
if(tree[li].r >= l) ans += search(li,l,r);
if(tree[ri].l <= r) ans += search(ri,l,r);
return ans;
}
- \(l <= tree[i].l\) && \(tree[i].r <= r\)
完全在区间内就直接返回 \(sum\)
- \(tree[li].r >= l\)
- \(tree[ri].l <= r\)
如果要往下面搜索
那就先要把 \(lazy\) 往下面推
然后在把这段的值加起来返回上去
Code
#include<bits/stdc++.h>
#define maxn 1000010
#define INF 1e12
#define mid ((l+r)>>1)
#define li i<<1
#define ri 1+(i<<1)
using namespace std;
int n,val[maxn];
struct Node{
int l,r,sum;
int k,lazy;
}tree[maxn];
void Read(){
cin >> n;
for(int i = 1;i <= n;i++)cin >> val[i];
}
void build(int i,int l,int r){
tree[i].l = l;
tree[i].r = r;
if(l == r){
tree[i].sum = val[l];
return ;
}
build(li,l,mid);
build(ri,mid+1,r);
tree[i].sum = tree[li].sum + tree[ri].sum;
return ;
}
void push(int i){
tree[li].lazy += tree[i].lazy;
tree[ri].lazy += tree[i].lazy;
int mid_ = (tree[i].l + tree[i].r) >> 1;
tree[li].sum += tree[i].lazy * (mid_ - tree[li].l + 1);
tree[ri].sum += tree[i].lazy * (tree[i].r - mid_);
tree[i].lazy = 0;
return ;
}
void add(int i,int l,int r,int k){
if(l <= tree[i].l && tree[i].r <= r){
tree[i].sum += k * (tree[i].r - tree[i].l + 1);
tree[i].lazy += k;
return ;
}
if(tree[i].lazy != 0) push(i);
if(tree[li].r >= l)
add(li,l,r,k);
if(tree[ri].l <= r)
add(ri,l,r,k);
tree[i].sum = tree[li].sum + tree[ri].sum;
return ;
}
int search(int i,int l,int r){
if(l <= tree[i].l && tree[i].r <= r)
return tree[i].sum;
push(i);
int ans = 0;
if(tree[li].r >= l) ans += search(li,l,r);
if(tree[ri].l <= r) ans += search(ri,l,r);
return ans;
}
void interaction(){
while(1){
int tot;
cin >> tot;
if(tot == 1){
int l,r;
cin >> l >> r;
cout << search(1,l,r) << endl;
} else if(tot == 2){
int l,r,k;
cin >> l >> r >> k;
add(1,l,r,k);
} else if(tot == 3){
return ;
}
}
}
int main(){
cout << "query section" << endl << "change section" << endl;
Read();
build(1,1,n);
cout << "query 1" << endl << "change 2" << endl << "break 3" << endl;
interaction();
return 0;
}
[C++]线段树 区间修改 区间查询的更多相关文章
- [线段树]区间修改&区间查询问题
区间修改&区间查询问题 [引言]信息学奥赛中常见有区间操作问题,这种类型的题目一般数据规模极大,无法用简单的模拟通过,因此本篇论文将讨论关于可以实现区间修改和区间查询的一部分算法的优越与否. ...
- SPOJ GSS2 - Can you answer these queries II(线段树 区间修改+区间查询)(后缀和)
GSS2 - Can you answer these queries II #tree Being a completist and a simplist, kid Yang Zhe cannot ...
- Hdu 1698(线段树 区间修改 区间查询)
In the game of DotA, Pudge's meat hook is actually the most horrible thing for most of the heroes. T ...
- SPOJ BGSHOOT - Shoot and kill (线段树 区间修改 区间查询)
BGSHOOT - Shoot and kill no tags The problem is about Mr.BG who is a great hunter. Today he has gon ...
- A Simple Problem with Integers POJ - 3468 线段树区间修改+区间查询
//add,懒标记,给以当前节点为根的子树中的每一个点加上add(不包含根节点) // #include <cstdio> #include <cstring> #includ ...
- I Hate It(线段树点修改区间查询)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1754 I Hate It Time Limit: 9000/3000 MS (Java/Others) ...
- Codeforces Round #442 (Div. 2) E Danil and a Part-time Job (dfs序加上一个线段树区间修改查询)
题意: 给出一个具有N个点的树,现在给出两种操作: 1.get x,表示询问以x作为根的子树中,1的个数. 2.pow x,表示将以x作为根的子树全部翻转(0变1,1变0). 思路:dfs序加上一个线 ...
- POJ.2528 Mayor's posters (线段树 区间更新 区间查询 离散化)
POJ.2528 Mayor's posters (线段树 区间更新 区间查询 离散化) 题意分析 贴海报,新的海报能覆盖在旧的海报上面,最后贴完了,求问能看见几张海报. 最多有10000张海报,海报 ...
- POJ.3468 A Simple Problem with Integers(线段树 区间更新 区间查询)
POJ.3468 A Simple Problem with Integers(线段树 区间更新 区间查询) 题意分析 注意一下懒惰标记,数据部分和更新时的数字都要是long long ,别的没什么大 ...
- codevs 1690 开关灯 线段树区间更新 区间查询Lazy
题目描述 Description YYX家门前的街上有N(2<=N<=100000)盏路灯,在晚上六点之前,这些路灯全是关着的,六点之后,会有M(2<=m<=100000)个人 ...
随机推荐
- zip文件结构
转starshine博客 一个zip文件由三个部分组成:压缩源文件数据区.压缩源文件目录区.压缩源文件目录结束标志 压缩源文件数据区: 50 4B 03 04:这是头文件标记(0x04034b50) ...
- 一个Web项目实现多个数据库存储数据并相互切换
1.使用场景 多数据源使用场景一般为: 主从数据库切换 读写分离 兼容旧库 2.具体实现 实现原理 Spring2.x的版本中采用Proxy模式,就是在方案中实现一个虚拟的数据源,并且用它来封装数据源 ...
- Blazor中CSS隔离无法用在子组件内部
Blazor的CSS隔离是个很好的东西,如图,只需添加一个与Razor组件同名的CSS文件,这个文件中的CSS样式只会在同名的Razor组件中使用. 原理是通过在dom元素添加一个代表标识符的属性 在 ...
- 文件系统:ext4 的 block 分布(1G分区为例)
总的block数量:262144 $ dumpe2fs /dev/vg01/test | grep "Block count" dumpe2fs 1.42.9 (28-Dec-20 ...
- python连接 Basler pylon相机遇到的问题
今天使用下图程序去连接相机 以下是摄像头IP参数 电脑IP参数 在确认电脑能够ping通相机的情况下 以及检查专用软件能否访问之后 依然遇到了以下错误 经过了多番调试之后发现即使能够ping通,子网掩 ...
- Redis从入门到放弃(3):发布与订阅
1.介绍 Redis是一个快速.开源的内存数据库,支持多种数据结构,如字符串.哈希.列表.集合.有序集合等.除了基本的数据存储和检索功能外,Redis还提供了许多高级功能,其中之一就是发布订阅(Pub ...
- 体验IntelliJ IDEA的远程开发(Remote Development)
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 IDEA的远程开发功能,可以将本地的编译.构 ...
- gitlab-runner-config-in-docker
gitlab in docker 网上有很多现成的解决方案,本文仅作流程梳理,若不需要,可直接用gitlab官方提供的镜像 installation Dockerfile FROM registry. ...
- 《UNIX 传奇:历史与回忆》读后感
<UNIX 传奇:历史与回忆> 是 bwk(Brian W. Kernighan)2019 年的新作,回忆了 UNIX 在大半个世纪的风雨历程,是一本引人入胜的书籍.通过对 UNIX 操作 ...
- Acwing 周赛88 题解
比赛链接 ·A题 题目描述 给定一个整数\(x\),请你找到严格大于\(x\)且各位数字均不相同的最小整数\(y\). \(1000 \le x \le 9000\) 做法分析 发现数据范围很小,那么 ...