线段树(四)——两个标记(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,若某段长 ...
随机推荐
- 微信小程序的calc不生效处理
大致文字初略的记录描述一下问题:外层是relative相对定位,内部一个view 需要绝对定位bottom的值为128rpx,同时还要兼容适配苹果x的底部,所以值是这样设置的: bottom: cal ...
- (模板)poj2947(高斯消元法解同余方程组)
题目链接:https://vjudge.net/problem/POJ-2947 题意:转换题意后就是已知m个同余方程,求n个变量. 思路: 值得学习的是这个模板里消元用到lcm的那一块.注意题目输出 ...
- 【计算机网络】-传输层-Internet传输协议-TCP
[计算机网络]-传输层-Internet传输协议-TCP TCP介绍 在不可靠的互联网上提供一个可靠的端到端字节流 面向连接的.可靠的.端到端的.基于字节流的传输协议 TCP位置 TCP服务模型 应用 ...
- thinkphp5分页查询paginate()传递参数
使用paginate()分页,我这里实现的是搜索后分页显示,翻页后传递搜索关键字 www.demo.com/home/search/?k=搜索关键字&page=2 搜索分页源码在: think ...
- idea插件,配置
1.插件 2.配置 1.去除double shift 全局搜索的功能,反正不符合我的习惯 Help -> Find Action 搜索 registry ,勾选 double.click.ha ...
- C# 批量设置窗体中控件状态的方法
在开发中常遇到当点击某个按钮的时候,禁用文本框或按钮的的状态,以防止误操作,下面的代码是我已批量设置指定控件中的按钮状态的代码,同理可以延伸出很多操作. /// <summary> /// ...
- python selenium4 模拟点击+拖动+保存验证码 测试对象+以验证码的返回ID保存命名 58同城验证码
#!/usr/bin/python # -*- coding: UTF-8 -*- # @Time : 2019/12/5 17:30 # @Author : shenghao/10347899@qq ...
- 基于SDP的提议/应答(offer/answer)模型简介
1.引入 在松耦合会议中,会话参数完全由会议创建者来确定,参与者能做的仅仅是根据这些会话参数来加入会议(当然也可以选择不加入).这种情况下,主要要做的就是会话描述,在这里SDP本身就足够了. 但是在更 ...
- scala的泛型浅析
1. scala泛型浅析 package com.dtspark.scala.basics /** * 1,scala的类和方法.函数都可以是泛型. * * 2,关于对类型边界的限定分为上边界和下边界 ...
- ES5中一些重要的拓展
1.对象的拓展 ①Object.create(obj, {age:{value:18, writable:true, configurable:true, enumerable:true}); 以指定 ...