线段树 区间修改 区间查询

请先阅读上一篇Bolg

算法思想

这次要引入一个核心变量:

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++]线段树 区间修改 区间查询的更多相关文章

  1. [线段树]区间修改&区间查询问题

    区间修改&区间查询问题 [引言]信息学奥赛中常见有区间操作问题,这种类型的题目一般数据规模极大,无法用简单的模拟通过,因此本篇论文将讨论关于可以实现区间修改和区间查询的一部分算法的优越与否. ...

  2. 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 ...

  3. Hdu 1698(线段树 区间修改 区间查询)

    In the game of DotA, Pudge's meat hook is actually the most horrible thing for most of the heroes. T ...

  4. 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 ...

  5. A Simple Problem with Integers POJ - 3468 线段树区间修改+区间查询

    //add,懒标记,给以当前节点为根的子树中的每一个点加上add(不包含根节点) // #include <cstdio> #include <cstring> #includ ...

  6. I Hate It(线段树点修改区间查询)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1754 I Hate It Time Limit: 9000/3000 MS (Java/Others) ...

  7. 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序加上一个线 ...

  8. POJ.2528 Mayor's posters (线段树 区间更新 区间查询 离散化)

    POJ.2528 Mayor's posters (线段树 区间更新 区间查询 离散化) 题意分析 贴海报,新的海报能覆盖在旧的海报上面,最后贴完了,求问能看见几张海报. 最多有10000张海报,海报 ...

  9. POJ.3468 A Simple Problem with Integers(线段树 区间更新 区间查询)

    POJ.3468 A Simple Problem with Integers(线段树 区间更新 区间查询) 题意分析 注意一下懒惰标记,数据部分和更新时的数字都要是long long ,别的没什么大 ...

  10. codevs 1690 开关灯 线段树区间更新 区间查询Lazy

    题目描述 Description YYX家门前的街上有N(2<=N<=100000)盏路灯,在晚上六点之前,这些路灯全是关着的,六点之后,会有M(2<=m<=100000)个人 ...

随机推荐

  1. 在HTML中引入React和JSX

    前言 Vue 可以非常方便地与 Pure HTML 结合,代替 jQuery 的功能,有一次遇到类似的场景时,我就想 React 能不能也以这种方式接入 HTML 网页,从而提高开发效率. 结果当然是 ...

  2. Powe AutoMate:列表操作

    大纲 记录对列表的操作 创建列表 向列表中添加元素 添加多个 合并列表 运行结果 反转列表 反转前 反转后 删除列表中的重复项 结果: 减去列表 结果:

  3. RocketMq消费原理及源码解析

    消费原理概览 先简单说下常见的rocketMq的部署方式,上图中broker为真正计算和存储消息的地方,而nameServer负责维护broker地 图中右侧consume message部分即是本文 ...

  4. 从ABNF读懂HTTP协议格式

    定义 HTTP(Hyper Text Transfer Protocol)超文本传输协议 HTML( Hyper Text Markup Language)超文本标记语言 URI(Uniform Re ...

  5. 明白error类型让你更快定位开发报错

    在javascript中,开发遇到的项目报错,很多时候都是通过 Error 这个类来展示的,清楚Error的类型可以更好定位项目中的问题 Error的类型 Error Error是所有其它错误的父类, ...

  6. 使用 Vue 实现页面访问拦截

    目录 1 Vue 路由与导航守卫 1.1 Vue 路由简介 1.2 导航守卫概述 2 实现访问拦截的核心概念 2.1 路由守卫介绍 2.1.1 前置守卫(beforeEach) 2.1.2 后置钩子( ...

  7. 学好Elasticsearch系列-索引的批量操作

    本文已收录至 Github,推荐阅读 Java 随想录 微信公众号:Java 随想录 先看后赞,养成习惯. 点赞收藏,人生辉煌. 目录 基于 mget 的批量查询 基于 bulk 的批量增删改 增加 ...

  8. 一文读懂LockSupport

    阅读本文前,需要储备的知识点如下,点击链接直接跳转. java线程详解 Java不能操作内存?Unsafe了解一下 LockSupport介绍 搞java开发的基本都知道J.U.C并发包(即java. ...

  9. Python 创建或读取 Excel 文件

    Excel是一种常用的电子表格软件,广泛应用于金融.商业和教育等领域.它提供了强大的数据处理和分析功能,可进行各种计算和公式运算,并能创建各种类型的图表和可视化数据.Excel的灵活性使其成为处理和管 ...

  10. 超全技术学习资料PDF分享

    技术学习资料分享,目前共20G,持续更新... Java学习资料: 大数据Hadoop: 这里不一一截图了,资源持续更新中. 关注下面公众号进行下载.