线段树 区间开方区间求和 & 区间赋值、加、查询
本文同步发表于 https://www.zybuluo.com/Gary-Ying/note/1288518
线段树的小应用 —— 维护区间开方区间求和
约定: sum(i,j)
表示区间 [i,j]
中所有元素的和,也就是\(\Sigma_{k=i}^j a_k\)
这个维护思想来自 分块 ;线段树维护区间开方的难点就在于我们没有办法很方便地维护区间的和,具体来说,如果我们对区间 [l,r]
进行开方,我们并不能从 sum(l,r)
推到 sum'(l,r)
。
这就比较麻烦了,我们不能很快地维护区间的和,而将来查询的时候又有可能会用到这一整段的和,所以我们必须维护它。
既然不能整段地去求,也就意味着我们只能 一个一个地求 ,我们只能对每个元素开方后再相加。
这是很暴力的做法,时间复杂度有保证吗?
如果单纯地每次修改都暴力修改,时间复杂度是 \(O(n^2)\) 的,但是我们发现,对于 int(long int)
范围内的数字,最多开 5 次二次方就会变成 0 或 1,也就是每个数字暴力开方的次数不会超过 5 次,这样时间复杂度就有保证了。
时间复杂度大概是 \(O(n log_2^n)\)。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
const int maxn = 50007;
int n, a[maxn], sum[maxn << 2];
bool flag[maxn << 2];
inline void PushUp(int rt){
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
flag[rt] = flag[rt<<1] & flag[rt<<1|1];
}
void build(int rt, int l, int r){
if (l == r){
sum[rt] = a[l];
flag[rt] = (a[l] == 0) || (a[l] == 1);
return;
}
int m = (l + r) >> 1;
build(rt << 1, l, m);
build(rt << 1 | 1, m + 1, r);
PushUp(rt);
}
inline void update(int rt, int l, int r){
for (int i = l; i <= r; ++i) a[i] = sqrt(a[i]);
build(rt, l, r);
}
void xSqrt(int rt, int l, int r, int L, int R){
if (L <= l && r <= R){
if (flag[rt]) return;
else update(rt, l, r);
return;
}
int m = (l + r) >> 1;
if (L <= m) xSqrt(rt<<1, l, m, L, R);
if (R > m) xSqrt(rt<<1|1, m+1, r, L, R);
PushUp(rt);
}
int query(int rt, int l, int r, int L, int R){
if (L <= l && r <= R) return sum[rt];
int m = (l + r) >> 1, res = 0;
if (L <= m) res += query(rt<<1, l, m, L, R);
if (R > m) res += query(rt<<1|1, m+1, r, L, R);
return res;
}
int main(){
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
build(1, 1, n);
for (int t = 1; t <= n; ++t){
int opt, l, r, c; scanf("%d%d%d%d", &opt, &l, &r, &c);
if (opt == 0) xSqrt(1, 1, n, l, r);
else printf("%d\n", query(1, 1, n, l, r));
}
return 0;
}
线段树的小应用 —— 区间赋值、加、查询
线段树对于一个区间有 多个 操作时,处理 操作的优先级 就显得十分重要了。
对于这道题,我们需要分析 赋值 和 加 两种操作谁的优先级更高比较好,换句话说,我们要考虑 如何合并懒惰标记 。为了维护区间赋值、加这两种操作,我们需要两个标记:修改标记 & 加标记。
标记的优先级指的是如果同样存在两个标记,先执行的标记是谁,举个栗子:如果定义 修改标记 优先级高,那么对于原值,操作后的值就是先修改再加;如果定义 加标记 优先级高,那么就是先加再修改,我们发现这时候加标记已经没有了意义,所以我们如果定义 修改标记 优先级高。
如果对于区间 \([L,R]\) ,已经有了修改标记:
- 如果要执行 修改操作 直接更新 修改标记
- 如果要执行 加操作 直接更新 修改标记
如果对于区间 \([L,R]\) ,已经有了加标记:
- 如果要执行 修改操作 直接更新 修改标记,清空 加标记
- 如果要执行 加操作 直接更新 加标记、
我们发现,加标记 和 修改标记 其实并不会共存。
END,请看代码
(相关题目:Luogu4315 月下“毛景树”,需要用线段树支持树剖)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 100007;
int Max[maxn << 2], changetag[maxn << 2], addtag[maxn << 2];
int a[maxn];
int n, Q;
inline void PushUp(int rt){
Max[rt] = max(Max[rt<<1], Max[rt<<1|1]);
}
inline void PushDown(int rt, int ln, int rn){
if (changetag[rt] != -1){
Max[rt<<1] = changetag[rt]; Max[rt<<1|1] = changetag[rt];
changetag[rt << 1] = changetag[rt]; addtag[rt << 1] = 0;
changetag[rt << 1 | 1] = changetag[rt]; addtag[rt << 1 | 1] = 0;
changetag[rt] = -1;
}else if (addtag[rt]){
Max[rt<<1] += addtag[rt]; Max[rt<<1|1] += addtag[rt];
if (changetag[rt<<1] != -1) changetag[rt<<1] += addtag[rt];
else addtag[rt<<1] += addtag[rt];
if (changetag[rt<<1|1] != -1) changetag[rt<<1|1] += addtag[rt];
else addtag[rt<<1|1] += addtag[rt];
addtag[rt] = 0;
}
}
void build(int rt, int l, int r){
changetag[rt] = -1; addtag[rt] = 0;
if (l == r){
Max[rt] = a[l];
return;
}
int m = (l + r) >> 1;
build(rt<<1, l, m);
build(rt<<1|1, m+1, r);
PushUp(rt);
}
void change(int rt, int l, int r, int L, int R, int C){
if (L <= l && r <= R){
Max[rt] = C;
changetag[rt] = C;
addtag[rt] = 0;
return;
}
int m = (l + r) >> 1;
PushDown(rt, m - l + 1, r - m);
if (L <= m) change(rt<<1, l, m, L, R, C);
if (R > m) change(rt<<1|1, m+1, r, L, R, C);
PushUp(rt);
}
void add(int rt, int l, int r, int L, int R, int C){
if (L <= l && r <= R){
Max[rt] = Max[rt] + C;
if (changetag[rt] == -1) addtag[rt] += C;
else changetag[rt] += C;
return;
}
int m = (l + r) >> 1;
PushDown(rt, m - l + 1, r - m);
if (L <= m) add(rt<<1, l, m, L, R, C);
if (R > m) add(rt<<1|1, m+1, r, L, R, C);
PushUp(rt);
}
int query(int rt, int l, int r, int L, int R){
if (L <= l && r <= R) return Max[rt];
int m = (l + r) >> 1, res = -1;
PushDown(rt, m - l + 1, r - m);
if (L <= m) res = max(res, query(rt<<1, l, m, L, R));
if (R > m) res = max(res, query(rt<<1|1, m+1, r, L, R));
return res;
}
int main(){
scanf("%d%d", &n, &Q);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
build(1, 1, n);
while (Q--){
int opt, l, r, val; scanf("%d%d%d%d", &opt, &l, &r, &val);
if (opt == 1) change(1, 1, n, l, r, val);
else if (opt == 2) add(1, 1, n, l, r, val);
else printf("%d\n", query(1, 1, n, l, r));
}
return 0;
}
线段树 区间开方区间求和 & 区间赋值、加、查询的更多相关文章
- HDU 1754 I Hate It(线段树之单点更新,区间最值)
I Hate It Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
- HDU1166(线段树 +更新单点,求区间总和)、HDU1754(线段树 + 更新单点,求区间最大值)
线段树简单应用 先附上几张图便与理解,大佬文章传送门1.传送门2 HDU1166:题目描述 线段树 +更新单点,求区间总和 代码如下(递归版) #include<iostream> #in ...
- HDU 4348 To the moon 可持久化线段树,有时间戳的区间更新,区间求和
To the moonTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/contest/view.a ...
- HDU - 1166 - 敌兵布阵 线段树的单点修改,区间求和
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> ...
- hdu 1698 线段树(成段替换 区间求和)
一条钩子由许多小钩子组成 更新一段小钩子 变成铜银金 价值分别变成1 2 3 输出最后的总价值 Sample Input11021 5 25 9 3 Sample OutputCase 1: The ...
- NOI2016区间bzoj4653(线段树,尺取法,区间离散化)
题目描述 在数轴上有 \(N\) 个闭区间 \([l_1,r_1],[l_2,r_2],...,[l_n,r_n]\) .现在要从中选出 \(M\) 个区间,使得这 \(M\) 个区间共同包含至少一个 ...
- POJ 2155 Matrix (二维线段树入门,成段更新,单点查询 / 二维树状数组,区间更新,单点查询)
题意: 有一个n*n的矩阵,初始化全部为0.有2中操作: 1.给一个子矩阵,将这个子矩阵里面所有的0变成1,1变成0:2.询问某点的值 方法一:二维线段树 参考链接: http://blog.csdn ...
- HDU 1166 敌兵布阵 线段树的基本应用——动态区间和问题
题目: http://acm.hdu.edu.cn/showproblem.php?pid=1166 简单题,1A了,这个好像就是传说中的“点树”. 设当前结点表示线段[left, right],编号 ...
- HDU - 1754 I Hate It (线段树单点修改,求区间最大值)
很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老师有 ...
- HDU(1754),线段树,单点替换,区间最值
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1754 线段树模板题,update功能是单点替换,query是访问区间最大值. #include < ...
随机推荐
- 启动多个logstash脚本
一台服务器上启动多个logstash脚本 # more logstash_click #!/bin/sh # Init script for logstash # Maintained by Elas ...
- GIT-常规操作
本地安装git, 安装文件: Git客户端: 可百度搜索:GIT64位或GIT32位等关键字找到相应的版本进行下载. 本地地址:D:\20-git\Git-2.20.1-64-bit.exe 也可百度 ...
- Maven 学习总结 (四)之 测试
使用Maven测试 Maven的重要职责之一就是自动运行单元测试.它通过maven-surefire-plugin与主流的单元测试框架JUnit3.JUnit4以及TestNG集成,并且能够自动生成丰 ...
- Spring的事务机制
---恢复内容开始--- 内定的=>(只需要在xml 中添加一个bean) 在xml 中添加 <bean id="listener" class="com.t ...
- JAVA IO练习
停车场有进场和出场的功能1. 进场时:采用键盘录入的方式,录入汽车的品牌.颜色.车牌号. 把品牌.颜色.车牌号,以及进场时间写入car.txt文件中. 2. 出场时:键盘录入车牌号,去文件中查找该车 ...
- Web概述
Web概述 1. JavaWeb 使用java开发的基于互联网的项目 2. 软件架构 C/S:客户端服务器架构 优点:用户体验好,很多数据在本地 缺点:安装.开发.部署.维护麻烦 B/S:浏览器服务器 ...
- Geometric regularity criterion for NSE: the cross product of velocity and vorticity 2: $u\times \om\cdot \n\times \om$
在 [Lee, Jihoon. Notes on the geometric regularity criterion of 3D Navier-Stokes system. J. Math. Phy ...
- [再寄小读者之数学篇](2014-05-27 矩阵的迹与 Jacobian)
(from MathFlow) 设 $A=(a_{ij})$, 且定义 $$\bex \n_A f(A)=\sex{\cfrac{\p f}{\p a_{ij}}}. \eex$$ 试证: (1) $ ...
- Matplotlib画图详解
from matplotlib import pyplot as plt #调节图形大小,宽,高 plt.figure(figsize=(6,9)) #定义饼状图的标签,标签是列表 labels = ...
- main 及Scanner
通过main方法的args数组可以从控制台获取一组字符串数据. 1.Scanner类用于扫描从控制台输入的数据,可以接收字符串和基本数据类型的数据. 2.Scanner类位于java.util.Sca ...