线段树(四)——两个标记(add和set)
add无序,set有序。规定同时有两个标记时,表示先执行set再执行add。
1. 更新操作:
int op,cl,cr,v;
void update(int o, int L, int R) {
int lc = o*, rc = o*+;
if(cl <= L && cr >= R) { // 标记修改
if(op == ) addv[o] += v;
else { setv[o] = v; addv[o] = ; }
} else {
pushdown(o);
int M = L + (R-L)/;
if(cl <= M) update(lc, L, M); else maintain(lc, L, M);
if(cr > M) update(rc, M+, R); else maintain(rc, M+, R);
}
maintain(o, L, R);
}
此操作中需要维护标记,这里保证了不会出现先有add再有set,这种情况只会保留set。
值得注意的是,标记下推时左右子树都需要维护,其中递归进入的子树会在递归结束时自然调用maintain函数,而另一个子树需要手动调用maintain。
2. 标记传递:
void pushdown(int o) {
int lc = o*, rc = o*+;
if(setv[o] >= ) {
setv[lc] = setv[rc] = setv[o];
addv[lc] = addv[rc] = ;
setv[o] = -; // 清除本结点标记
}
if(addv[o]) {
addv[lc] += addv[o];
addv[rc] += addv[o];
addv[o] = ; // 清除本结点标记
}
}
set和add分别讨论,结点o有标记时才下推。
3. 维护信息:
void maintain(int o, int L, int R) {
int lc = o*, rc = o*+;
if(R > L) {
sumv[o] = sumv[lc] + sumv[rc];
minv[o] = min(minv[lc], minv[rc]);
maxv[o] = max(maxv[lc], maxv[rc]);
}
if(setv[o] >= ) { minv[o] = maxv[o] = setv[o]; sumv[o] = setv[o] * (R-L+); }
if(addv[o]) { minv[o] += addv[o]; maxv[o] += addv[o]; sumv[o] += addv[o] * (R-L+); }
}
注意:所有叶子上总是保留set标记(初始化的)而不会被清除(pushdown只能针对非叶结点),因此maintain函数对于叶子来说并不会重复累加addv[o].
4. 查询信息:
int ql, qr;
void query(int o, int L, int R, int& ssum, int& smin, int &smax) {
int lc = o*, rc = o*+;
maintain(o, L, R); // 处理被pushdown下来的标记
if(ql <= L && qr >= R) {
ssum = sumv[o];
smin = minv[o];
smax = maxv[o];
} else {
pushdown(o);
int M = L + (R-L)/;
int lsum = , lmin = INF, lmax = -INF;
int rsum = , rmin = INF, rmax = -INF;
if(ql <= M) query(lc, L, M, lsum, lmin, lmax); else maintain(lc, L, M);
if(qr > M) query(rc, M+, R, rsum, rmin, rmax); else maintain(rc, M+, R);
ssum = lsum + rsum;
smin = min(lmin, rmin);
smax = max(lmax, rmax);
}
}
也要维护信息和下推标记。
5. 初始化:
通过add,当然,也可以通过set.
memset(setv, , sizeof(setv)); //保证叶子结点的set标签
for(int i = ; i <= n;i++)
{
scanf("%d", &v);
cl = cr = i; v = a[i]; op=;
update(, , n);
}
#include<bits/stdc++.h>
using namespace std; const int INF = 0x3f3f3f3f;
const int maxn = + ;
const int maxnode = maxn << ;
int sumv[maxnode], minv[maxnode], maxv[maxnode], setv[maxnode], addv[maxnode];
int n, a[maxn]; // 维护信息
void maintain(int o, int L, int R) {
int lc = o*, rc = o*+;
if(R > L) {
sumv[o] = sumv[lc] + sumv[rc];
minv[o] = min(minv[lc], minv[rc]);
maxv[o] = max(maxv[lc], maxv[rc]);
}
if(setv[o] >= ) { minv[o] = maxv[o] = setv[o]; sumv[o] = setv[o] * (R-L+); }
if(addv[o]) { minv[o] += addv[o]; maxv[o] += addv[o]; sumv[o] += addv[o] * (R-L+); }
} // 标记传递
void pushdown(int o) {
int lc = o*, rc = o*+;
if(setv[o] >= ) {
setv[lc] = setv[rc] = setv[o];
addv[lc] = addv[rc] = ;
setv[o] = -; // 清除本结点标记
}
if(addv[o]) {
addv[lc] += addv[o];
addv[rc] += addv[o];
addv[o] = ; // 清除本结点标记
}
} int op,cl,cr,v;
void update(int o, int L, int R) {
int lc = o*, rc = o*+;
if(cl <= L && cr >= R) { // 标记修改
if(op == ) addv[o] += v;
else { setv[o] = v; addv[o] = ; }
} else {
pushdown(o);
int M = L + (R-L)/;
if(cl <= M) update(lc, L, M); else maintain(lc, L, M);
if(cr > M) update(rc, M+, R); else maintain(rc, M+, R);
}
maintain(o, L, R);
} int ql, qr;
void query(int o, int L, int R, int& ssum, int& smin, int &smax) {
int lc = o*, rc = o*+;
maintain(o, L, R); // 处理被pushdown下来的标记
if(ql <= L && qr >= R) {
ssum = sumv[o];
smin = minv[o];
smax = maxv[o];
} else {
pushdown(o);
int M = L + (R-L)/;
int lsum = , lmin = INF, lmax = -INF;
int rsum = , rmin = INF, rmax = -INF;
if(ql <= M) query(lc, L, M, lsum, lmin, lmax); else maintain(lc, L, M);
if(qr > M) query(rc, M+, R, rsum, rmin, rmax); else maintain(rc, M+, R);
ssum = lsum + rsum;
smin = min(lmin, rmin);
smax = max(lmax, rmax);
}
} void print_debug(int o, int L, int R)
{
printf("o:%d L:%d R:%d setv:%d addv:%d minv:%d\n", o, L, R, setv[o], addv[o], minv[o]);
if(R > L)
{
int M = L + (R - L) / ;
print_debug(*o, L, M);
print_debug(*o+, M+, R);
}
} int main()
{
memset(setv, , sizeof(setv)); printf("数组元素个数:");
scanf("%d", &n);
printf("数组元素:");
for(int i = ; i <= n;i++)
{
scanf("%d", &a[i]);
cl = cr = i; v = a[i]; op=;
update(, , n);
} printf("1代表查询,2代表增加,3代表设置\n");
printf("选择:");
int chose;
while(scanf("%d", &chose) == )
{
if(chose == )
{
printf("查询的左右区间:");
scanf("%d%d", &ql, &qr);
int ssum, smin, smax;
query(, , n, ssum, smin, smax);
printf("最小值:%d\n", smin);
print_debug(, , n);
}
else if(chose == )
{
printf("左右区间和增加值:");
scanf("%d%d%d", &cl, &cr, &v);
op = ;
update(, , n);
print_debug(, , n);
}
else
{
printf("左右区间和设置值:");
scanf("%d%d%d", &cl, &cr, &v);
op = ;
update(, , n);
print_debug(, , n);
}
printf("选择:");
} return ;
}
完整代码
线段树(四)——两个标记(add和set)的更多相关文章
- CodeForces Round #179 (295A) - Greg and Array 一个线段树做两次用
线段树的区间更新与区间求和...一颗这样的线段树用两次... 先扫描1~k...用线段树统计出每个操作执行的次数... 那么每个操作就变成了 op. l , op.r , op.c= times* ...
- [HDOJ4578]Transformation(线段树,多延迟标记)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4578 四种操作:查询.加法.乘法.改数.应该是需要维护三个lazy标记,然后就是套路了.查询是区间内所 ...
- 【BZOJ-4636】蒟蒻的数列 动态开点线段树 ||(离散化) + 标记永久化
4636: 蒟蒻的数列 Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 247 Solved: 113[Submit][Status][Discuss ...
- 【HDU 4614】Vases and Flowers(线段树区间更新懒惰标记)
题目0到n-1的花瓶,操作1在下标a开始插b朵花,输出始末下标.操作2清空[a,b]的花瓶,求清除的花的数量.线段树懒惰标记来更新区间.操作1,先查询0到a-1有num个空瓶子,然后用线段树的性质,或 ...
- 扶桑号战列舰 (单调栈+线段树区间更新懒惰标记 or 栈)
传送门 •题目描述 题目描述 众所周知,一战过后,在世界列强建造超无畏级战列舰的竞争之中,旧日本海军根据“个舰优越主义”,建造了扶桑级战列舰,完工时为当时世界上武装最为强大的舰只. 同时,扶桑号战列舰 ...
- HDU 4553 约会安排(线段树区间合并+双重标记)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4553 题目大意:就是有三种操作: ①DS x,安排一段长度为x的空闲时间跟屌丝一起,输出这段时间的起点 ...
- HDU 3911 Black And White (线段树区间合并 + lazy标记)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3911 给你n个数0和1,m个操作: 0操作 输出l到r之间最长的连续1的个数 1操作 将l到r之间 ...
- 杭电 HDU ACM 1698 Just a Hook(线段树 区间更新 延迟标记)
欢迎"热爱编程"的高考少年--报考杭州电子科技大学计算机学院 Just a Hook Time Limit: 4000/2000 MS (Java/Others) Memor ...
- 【codevs2216】行星序列 线段树 区间两异同修改+区间求和*****
[codevs2216]行星序列 2014年2月22日3501 题目描述 Description “神州“载人飞船的发射成功让小可可非常激动,他立志长大后要成为一名宇航员假期一始,他就报名参加了“小小 ...
- FZU-1608 Huge Mission 线段树(更新懒惰标记)
题目链接: https://cn.vjudge.net/problem/FZU-1608 题目大意: 长度n,m次操作:每次操作都有三个数:a,b,c:意味着(a,b]区间单位长度的价值为c,若某段长 ...
随机推荐
- superslider样式
.picScroll-left{ width: 1180px; position:relative; overflow: hidden; .bd{ ul{ li{ float: left; displ ...
- 数据库连接池——C3P0&Druid(快速入门)
数据库连接池--C3P0&Druid (一) 数据库连接池 每一个事物都有其存在的意义,在初学jdbc的时候,我们建立数据库连接对象后,会对其进行释放,但是数据库连接的建立和关闭是非常消耗资源 ...
- mysql语句(二)
--MySQL 连接的使用 JOIN 按照功能大致分为如下三类: INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录. LEFT JOIN(左连接):获取左表所有记录,即使右表 ...
- 一个复杂关联的sql
在项目中遇到了一个比较复杂关系的sql,关联关系有些模糊,现在梳理一下 sql如下: SELECT TRAN.TRANS_DATE, TRAN.TRANS_TIME, TRAN.BUSI_TRAC_C ...
- 7-MySQL DBA笔记-研发规范
第7章 研发规范 本章将为读者解读一份研发规范.为了更好地协同工作和确保所开发的应用尽可能的稳定.高效,建立一套数据库相关的研发规范是很有必要的,虽然研发规范的确立和推广是一项很耗时的工作,但所取得的 ...
- 1.DOS常用命令
d:+ 回车:盘符切换,进入D:盘 dir(directory):列出当前目录下的文件及文件夹md(make director):创建目录rd(remove director):删除目录(不能删除非空 ...
- Docker Registry搭建
一.前言 Docker官方镜像仓库 访问速度很慢,Docker Registry允许搭建我们自己的镜像仓库,为实现镜像拉取.推送提供便利. 二.安装与启动 1.创建目录 mkdir /usr/loca ...
- VUE实现简单的全选/全不选
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- 有趣的"=="与"==="
console.log([]==![]);//true //"=="会进行类型转换,转换成统一类型进行比较 // !符号优于==,[]boolean值为TRUE,所以![]就是FA ...
- 微信小程序编译提示tabar.iconPath 文件不存在
tabBar.list[0].iconPath 文件不存在 明明是按路径放了本地图片的,却依然显示路径不存在 需要把路径的图片转移到编译后的weapp文件中相同路径下的img文件中 本地正常路径 粘贴 ...