线段树 区间开方区间求和 & 区间赋值、加、查询
本文同步发表于 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 < ...
随机推荐
- JS学习笔记Day16
一.匀速运动 保证速度不让用户提供,将速度写到函数中 speed = target-obj.offsetLeft>0 ? 正速度 :负速度 二.缓冲运动 var speed=(target-ob ...
- 深入理解pthread_cond_wait、pthread_cond_signal
===============================man pthread_cond_wait的解释========================== LINUX环境下多线程编程肯定会遇到 ...
- EM算法(Expectation Maximization Algorithm)初探
1. 通过一个简单的例子直观上理解EM的核心思想 0x1: 问题背景 假设现在有两枚硬币Coin_a和Coin_b,随机抛掷后正面朝上/反面朝上的概率分别是 Coin_a:P1:-P1 Coin_b: ...
- 跟踪调试JDK源码时遇到的问题及解决方法
目录 问题描述 解决思路 在IntelliJ IDEA中调试JDK源码 在eclipse中调试JDK源码 总结 问题描述 最近在研究MyBatis的缓存机制,需要回顾一下HashMap的实现原理.于是 ...
- [Luogu P1516]青蛙的约会
按照题意,显然可以列出同余方程,k即为所求天数,再将其化为不定方程 ,那么对这个方程用扩展欧几里德算法即可得出k,q的一组解,但是方程有解的充要条件是(m – n) 和L不同时为零并且x – y是m ...
- [再寄小读者之数学篇](2014-09-22 distributions and square integrable functions)
Suppose that $f\in L^2$, $g\in \scrD'$, if $$\bex f=g,\mbox{ in }\scrD', \eex$$ then $f=g\in L^2$. I ...
- $A,B$ 实对称 $\ra\tr((AB)^2)\leq \tr(A^2B^2)$
设 $A,B$ 是 $n$ 阶实对称矩阵. 试证: $\tr((AB)^2)\leq \tr(A^2B^2)$. 又问: 等号何时成立? 证明: 由 $$\bex \sum_i \sez{\su ...
- Javaweb学习笔记——(二十八)——————Servlet3.0、动态代理、类加载器
web最后一天:完了. Servlet3.0 一.要求 1.MyEclipse10.0或以上版本 2.发布到Tomcat7.0或以上版本 二.步骤 ...
- bilibili存储型xss (绕过长度限制打乱顺序限制)
在个人空间的我的收藏中可编辑视频收藏的名称,之后尝试写入标签. http://space.bilibili.com/ 发现输出到前端的尖括号被转义了,不过出现了一个json接口,他的Content-T ...
- 使用工具intellij idea 进行java web开发简介
一.工具下载及安装准备 1.首先下载工具 Intellij idea ,下载地址:https://www.jetbrains.com/ 2.破解 百度下载一个 JetbrainsCrack-2.7- ...