线段树 区间修改 区间查询

请先阅读上一篇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. Matlab背景颜色修改

    背景 将修改内容添加到matlab的matlab.prf文件中,文件路径为在matlab中运行prefdir的结果,直接添加这些内容保存就好. github主题制作地址,里面有多种matlab主题配色 ...

  2. Seeion相关

    存储会话数据有两种方式: Cookie 1) 存储在浏览器端,通过服务器发送cookie数据 2) 使用cookie存储会话数据,相对不安全(可以cookie查到一些用户安全) 3) 从存储数据类型来 ...

  3. Python日志模块:实战应用与最佳实践

    本文详细解析了Python的logging模块,从基本介绍到实际应用和最佳实践.我们通过具体的代码示例解释了如何高效地使用这个模块进行日志记录,以及如何避免常见的陷阱,旨在帮助读者更好地掌握这个强大的 ...

  4. Appium新版本引发的一个问题

    Appium新版本引发的一个问题 准备工作 测试代码 from appium import webdriver des_cap = {'platformName': 'android'} driver ...

  5. (四) MdbCluster分布式内存数据库——业务消息处理

    (四) MdbCluster分布式内存数据库--业务消息处理   上篇:(三) MdbCluster分布式内存数据库--节点状态变化及分片调整   离上次更新文章已有快5个月,我还是有点懒.但我们系统 ...

  6. Windows查询进程和杀死进程

    查询进程 查询进程占用的端口(通过端口查询) netstat -ano |findstr "端口号" 杀死进程 通过进程号查看所属进程名称 tasklist |findstr &q ...

  7. linux 脚本:iptables-nat.sh

    #!/bin/bash # 2022.2.28 by dewan # DNAT configuration. iptables -t nat -F PUB_IFACE="enp125s0f0 ...

  8. HBase Compaction 原理与线上调优实践

    作者:vivo 互联网存储技术团队- Hang Zhengbo 本文对 HBase Compaction 的原理.流程以及限流的策略进行了详细的介绍,列举了几个线上进行调优的案例,最后对 Compac ...

  9. 【技术积累】Linux中的命令行【理论篇】【三】

    apt-get命令 命令介绍 Debian Linux发行版中的APT软件包管理工具,apt-get命令 是Debian Linux发行版中的APT软件包管理工具.所有基于Debian的发行都使用这个 ...

  10. Unity的IFilterBuildAssemblies:深入解析与实用案例

    Unity IFilterBuildAssemblies Unity IFilterBuildAssemblies是Unity引擎中的一个非常有用的功能,它可以让开发者在构建项目时自定义哪些程序集需要 ...