线段树 区间开方区间求和 & 区间赋值、加、查询
本文同步发表于 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 < ...
随机推荐
- Vue(小案例_vue+axios仿手机app)_图文列表实现
一.前言 1.导航滑动实现 2.渲染列表信息时让一开始加载的时候就请求数据 3.根据路由的改变,加载图文的改变(实现用户访问网站时可能执行的三个操作) 二.主要内容 1.导航滑动实现: (1)演示 ...
- <五>企业级开源仓库nexus3实战应用–使用nexus3配置npm私有仓库
一两个星期之前,你如果在我跟前说起私服的事情,我大概会绕着你走,因为我对这个东西真的一窍不通.事实上也正如此,开发同学曾不止一次的跟我说公司的私服版本太旧了,许多新的依赖编译之后不会从远程仓库自动缓存 ...
- 前端面试题整理—JavaScript篇(二)
1.使用js实现一个可持续的动画 2.实现一个可以自由拖动的悬浮框 3.实现一个倒计时效果 4.使用js仿写一个原生下拉列表框 5.创建10个<a>标签,点击的时候弹出对应的序号 6.实现 ...
- Node.js实战项目学习系列(3) CommonJS 模块化规范
前言 想开始编写Node.js代码,那么我们就必须先熟悉它的模块化规范CommonJS,本文将详细讲解CommonJS规范 本文代码 >>> github 地址 CommonJS N ...
- react+antdesign
http://scaffold.ant.design/#/scaffolds/ng-alain http://scaffold.ant.design/#/scaffolds/react-redux-a ...
- hadoop集群完全分布式搭建
Hadoop环境搭建:完全分布式 集群规划: ip hostname 192.168.204.154 master namenode resour ...
- Python中__get__, __getattr__, __getattribute__的区别及延迟初始化
本节知识点 1.__get__, __getattr__, __getattribute__的区别 2.__getattr__巧妙应用 3.延迟初始化(lazy property) 1.__get__ ...
- C语言网 蓝桥杯 1117K-进制数
这是一道较难的题目,我刚开始用排列组合的方式来做,并没有做出来,故运用了的深搜算法. 深搜算法的概念: 选其中一条路,遍历完成后,逐步返回直至全部遍历,最后返回起点. 解题思路 : 题目中对零的个数没 ...
- 让你爱不释手的 Python 模块
 一. logzero 在一个完整的信息系统里面,日志系统是一个非常重要的功能组成部分.它可以记录下系统所产生的所有行为.我们可以使用日志系统所记录的信息为系统进行排错,优化系统的性能,或者根据这些 ...
- lua分割字符串
str = "abc;123;345" local tab = string.split(str, ";") 然后list里面就是 abc123345 了.第二 ...