线段树 区间开方区间求和 & 区间赋值、加、查询
本文同步发表于 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 < ...
随机推荐
- MFC(2):Edit Control 实现自动换行
--------------------------------------- 设置属性: multiline: true Auto_HScroll:true Vertical scroll: tr ...
- Spark Standalone spark-env.sh
export JAVA_HOME=/app/jdk export SPARK_MASTER_PORT=7077 export SPARK_MASTER_WEBUI_PORT=8080 export S ...
- 第十节: EF的三种追踪实体状态变化方式(DBEntityEntry、ChangeTracker、Local)
一. 简介 我们在前面章节介绍EF基本增删改的时候,曾说过EF的SaveChanges()方法,会一次性的将所有的实体的状态变化统一提交到数据库,那么你是否想过EF的实体会有哪些状态变化呢?什么原因会 ...
- cookie、LocalStorage、sessionStorage三者区别以及使用方式
cookie用来保存客户浏览器请求服务器页面的请求信息 HTML5的WebStorage提供了两种API:localStorage(本地存储)和sessionStorage(会话存储) WebStor ...
- 使用Maven插件构建Spring Boot应用程序镜像
使用Maven插件构建Spring Boot应用程序的Docker镜像. 环境准备 1.Linux系统 2.安装JDK,Maven 3.安装Docker 应用实践 1.在应用程序根目录下添加Docke ...
- MySQL学习1 - 基本mysql语句
一 操作文件夹(数据库) 增 查 改 删 二 操作文件(数据表) 增 查 改 删 三 操作文件内容(数据记录) 增 查 改 删 一 操作文件夹(数据库) 增 create database db1 c ...
- oracle建表 和 设置主键自增
1.新建table CREATE TABLE ysb_log( id ) primary key not null , tbdate ) NULL, tb_time ) NOT NULL, tblog ...
- Go 环境配置
1.下载SDK 官网:https://golang.google.cn/ go中文在线文档:https://studygolang.com/pkgdoc 2.配置环境变量 windows使用.msi一 ...
- LNMP环境搭建:Nginx安装、测试与域名配置
Nginx作为一款优秀的Web Server软件同时也是一款优秀的负载均衡或前端反向代理.缓存服务软件 2.编译安装Nginx (1)安装Nginx依赖函数库pcre pcre为“perl兼容正则表达 ...
- windows :config windows update … 一直处于假死状态
参考文章:http://www.cnblogs.com/teacat/p/9204225.html 环境:win7 64bit 旗舰版 问题:重启后,系统更新到35%后,一直处于假死状态,未能正确进入 ...